-
Notifications
You must be signed in to change notification settings - Fork 32
first stab at floating-point comparators #209
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
lib/src/arithmetic/signals/floating_point_logics/floating_point_logic.dart
Outdated
Show resolved
Hide resolved
lib/src/arithmetic/signals/floating_point_logics/floating_point_logic.dart
Outdated
Show resolved
Hide resolved
lib/src/arithmetic/signals/floating_point_logics/floating_point_logic.dart
Outdated
Show resolved
Hide resolved
lib/src/arithmetic/signals/floating_point_logics/floating_point_logic.dart
Show resolved
Hide resolved
fp1.neq(fp2).value.toBool(), isTrue); // This will use Logic.neq() | ||
} else { | ||
// rare that the two numbers would collide but just to be safe | ||
expect(fp1.eq(fp2).value.toBool(), isTrue); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TODO: rest of the testing for the operators? we should definitely test equality right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So LogicStructure does not support operator != or operator ==.
When I try to add, it complains in FloatingPointValue about need for non-nullable type and a hashcode.
operator != is not overridable apparently.
operator -() -- the linter cannot decide if this should have override or not!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I never played with the -
operator, interesting.
You should not override the ==
operator since we want that to stay as object equality. I meant we should be sure to test that eq
works, and also some specific corner cases (e.g. NaN, infinities, etc.) and some of the exceptions that we're checking (mismatch widths, jbit-ness, etc.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll be adding the corner case testing. Infinities are always equal (modulo the sign), but NaNs are never equal.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we not do this (already in the code):
floating_point_value.dart
@override
bool operator ==(Object other) {
if (other is! FloatingPointValue) {
return false;
}
if ((exponent.width != other.exponent.width) |
(mantissa.width != other.mantissa.width)) {
return false;
}
if (isNaN != other.isNaN) {
return false;
}
if (isAnInfinity != other.isAnInfinity) {
return false;
}
if (isAnInfinity) {
return sign == other.sign;
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Another weird case: I thought we could not override <= or <:
fixed_point_value.dart:
@override
bool operator ==(Object other) {
if (other is! FloatingPointValue) {
return false;
}
if ((exponent.width != other.exponent.width) |
(mantissa.width != other.mantissa.width)) {
return false;
}
if (isNaN != other.isNaN) {
return false;
}
if (isAnInfinity != other.isAnInfinity) {
return false;
}
if (isAnInfinity) {
return sign == other.sign;
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes we should on the *Value
s, but not on the things implementing Logic
. The values are sort of like immutable software primitives, whereas signals are objects that change value over time during simulation.
lib/src/arithmetic/signals/floating_point_logics/floating_point_logic.dart
Outdated
Show resolved
Hide resolved
lib/src/arithmetic/signals/floating_point_logics/floating_point_logic.dart
Show resolved
Hide resolved
|
||
/// Negate the [FloatingPoint]. | ||
FloatingPoint operator -() => negate(); | ||
// ignore the lint warning about overriding the '-' operator. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what lint warning? does the analyzer flag a lint or not, because it should be failing if you don't have the right ignore syntax
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The analyzer flags a lint error, but it doesn't fail in CI. It is a false error, the operator works. I don't have to have this operator. I see a blue underline in my VSCode with the message that this operator needs an @override
annotation
When I add override, it still complains, probably because LogicValue doesn't have a '-' operator:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hmm weird! i wonder if it's an analyzer bug?
lib/src/arithmetic/signals/floating_point_logics/floating_point_logic.dart
Show resolved
Hide resolved
sign: sign.isZero ? LogicValue.one : LogicValue.zero, | ||
exponent: exponent, | ||
mantissa: mantissa); | ||
|
||
// Move this to FixedPointValuePopulator.ofFloatingPointValue. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
resolve this comment?
also, wouldn't we always want a fixed width FixedPointValue
per widths of the FloatingPointValue
components rather than the width changing based on values?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The width of FixedPointValue would be fixed but very wide to make FP conversion lossless. But that width is a function of the FP width (bias * 2 + mantissaWidth + 2): the full range of sliding an FP number up and down the scale of exponents plus its mantissa plus a jbit plus a sign bit.
So I thought it made sense to do a .toFixedPointValue rather than doing an .ofFloatingPointValue since we need to compute the widths dependent on the FP exponent/mantissa widths (not values).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe I didn't understand this question, but this function is dependent on FP component widths and is written to handle that -- did you see something value-based in the method?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, now I see. My comment was misleading and confused. I do NOT want to build anything that is value dependent. I was considering it when I could not figure out just how wide I had to go to cover the full range.
lib/src/arithmetic/values/floating_point_values/floating_point_value_populator.dart
Outdated
Show resolved
Hide resolved
@@ -10,6 +10,19 @@ import 'package:rohd/rohd.dart'; | |||
/// Computes the bit width needed to store [w] addresses. | |||
int log2Ceil(int w) => (log(w) / log(2)).ceil(); | |||
|
|||
/// Finds the index of the first set bit in a [BigInt] number. | |||
/// Returns length + 1 if there are no set bits (i.e., the number is zero). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
isn't this an infinite loop if inNumber
is 0?
wouldn't null
be a better return value if none is found?
also, is this a utility we want to expose in the public API? we have other things like Find
which can also find the n
th instance of a 1 or 0, plus an error signal on the hardware side. Maybe a more complete extension to LogicValue
would be more valuable as a public API?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like the idea of a LogicValue extension. We could mirror 'Find' in the Value side. Let me take a look.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For now I added code to match my other leading1 detectors. For some reason I have not tripped on this bug.
It makes sense to move to Find.
: fxdMantissa; // make sure the sign is correct | ||
final shift = exponent.toInt() - 1; | ||
final shiftedFxdMantissa = | ||
exponent.toInt() > 0 ? fxdMantissa << shift : fxdMantissa; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are we shifting out the unbiased exponent here? I would think you would need to debias the exponent but maybe I'm missing something further here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, its really weird: I am using the unbiased exponent of 00...001 to be fully right-shifted mantissa and then 11....10 to be the full left-shifted mantissa stored in a really wide FixedPoint integer. We would set n/m to be around the midpoint of this super-wide LogicStructure.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh okay, I would for sure need that explained to me 😆
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I answered poorly. yes, we are shifting the mantissa by the unbiased exponent. That way, the tiniest numbers are all the way to the right (from mantissa-1 to 0). Then the largest numbers are shifted as far left as we can so the mantissa sits at the left edge down to left edge - mantissa-width. that makes the fixed-point around 2*bias + mantissa-width wide.
I converted this PR to a draft as there is a lot to fix. |
Description & Motivation
This is a baseline for making arithmetic types have basic operations like comparison and negate. This provides these operations
for FloatingPoint.
Related Issue(s)
#199
Testing
All added operands have basic testing.
Backwards-compatibility
No
Documentation
No. This is an extension to the API for these types but a very natural one whose documentation is in the class itself.