You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Pioneer has its own fixed-precision arifmetic implementation. The code can be found in src/fixed.h
Usually we use 32 bits before the "decimal point" and 32 bits after, but StarSystemGenerator uses 16 bits before and 48 after.
The operator/ in fixed.h has multiple bugs.
Using C++20 rules
This is undefined behaviour (signed integer overflow) if d is INT64_MIN
if (d < 0) {
d = -d;
isneg = 1;
}
This is undefined behaviour (signed integer overflow) if isneg is true and Sint64(quotient_lo) is INT64_MIN
Assuming I correctly understand what the unary minus does for unsigned types,
the only difference between Uint64(-Sint64(quotient_lo)) and -quotient_lo is that
the former trigger undefined behaviour if quotient_lo is exactly 1 << 63
remainder can become negative, causing incorrect results. Here is one example, but any value of b such that abs(b.v) > (1 << 62) can be problematic.
Let FRAC be 32
Let a be 0x4000'0000.0000'0000 and b be 0x4000'0000.0000'0001 (Note the "hexadecimal point")
C++17 is much less permissive than C++20 when it comes to <<, >> and unsigned-to-signed conversions:
a.v >> (64 - FRAC) is implementation-defined if a.v < 0
a.v << FRAC is undefined if a.v < 0
quotient_hi <<= 1; is implementation-defined if the mathematical value of quotient_hi * 2 is greater than INT64_MAX (because the defenition of << references unsigned-to-signed conversion)
quotient_hi <<= 1; is undefined if quotient_hi < 0
Sint64(quotient_lo) is implementation-defined if quotient_lo is greater than INT64_MAX
The return statement converts Uint64 to fixedf, which requires an implicit conversion to Sint64. The conversion is implementation-defined if the returned value is greater than INT64_MAX
3 and 4 are triggered multiple times for every a except 0.
Pioneer has its own fixed-precision arifmetic implementation. The code can be found in src/fixed.h
Usually we use 32 bits before the "decimal point" and 32 bits after, but StarSystemGenerator uses 16 bits before and 48 after.
The operator/ in fixed.h has multiple bugs.
Using C++20 rules
This is undefined behaviour (signed integer overflow) if d is INT64_MIN
This is undefined behaviour (signed integer overflow) if isneg is true and Sint64(quotient_lo) is INT64_MIN
Here is what I find ironic:
Assuming I correctly understand the rules for the ternary operator with mixed types (a very optimistic assumption),
the code above is equivalent to
Assuming I correctly understand what the unary minus does for unsigned types,
the only difference between
Uint64(-Sint64(quotient_lo))
and-quotient_lo
is thatthe former trigger undefined behaviour if quotient_lo is exactly
1 << 63
remainder
can become negative, causing incorrect results. Here is one example, but any value of b such thatabs(b.v) > (1 << 62)
can be problematic.0x4000'0000.0000'0000
and b be0x4000'0000.0000'0001
(Note the "hexadecimal point")0x0000'0000.ffff'ffff
Here is a proof of concept on godbolt
Most combinarions of a negative
a
with anyb
produce incorrect results. It puzzles me greatly that no one noticed. Example:0x5555'5554.5555'5555
Here is a proof of concept on godbolt
Style notes
int isneg
should bebool isneg
To me this code:
Looks as if it ment this:
But it actually means:
Using C++17 rules
Pioneer currently targets the C++17 standard:
pioneer/CMakeLists.txt
Line 5 in 97d7be6
C++17 is much less permissive than C++20 when it comes to
<<
,>>
and unsigned-to-signed conversions:a.v >> (64 - FRAC)
is implementation-defined ifa.v < 0
a.v << FRAC
is undefined ifa.v < 0
quotient_hi <<= 1;
is implementation-defined if the mathematical value ofquotient_hi * 2
is greater than INT64_MAX (because the defenition of<<
references unsigned-to-signed conversion)quotient_hi <<= 1;
is undefined ifquotient_hi < 0
Sint64(quotient_lo)
is implementation-defined if quotient_lo is greater than INT64_MAXa
except 0.Note
I think most of this issues can be fixed by using only Uint64 inside this function.
Safely obtaining the modulus of a signed integer as an unsigned integer
Adapend for out needs:
The text was updated successfully, but these errors were encountered: