0

I'm worried that something like 7/2 might become 3.49999999 instead of 3.5 on some computer then round to 3 instead of 4. Is that possible?

I need every computer to re-run the same math exactly. That's the only time I ever have a float: Math.round(intA/intB)

Curtis
  • 2,486
  • 5
  • 40
  • 44
  • Does this answer your question? [Is floating point math broken?](https://stackoverflow.com/questions/588004/is-floating-point-math-broken) – OldProgrammer Mar 28 '23 at 22:20
  • 1
    I assume you mean `7/2` might become one of those values? – ShadowRanger Mar 28 '23 at 22:21
  • 2
    It will be the same on all devices because JS uses the IEEE754 standard. It’s called a standard for good reason. – Terry Mar 28 '23 at 22:23
  • Yes I mean 7/2 or anything that should have .5 at the end could ever be .49999 instead. If they all use the same standard I guess I don't have to worry about some device replaying my data differently for some users, thanks! – Curtis Mar 28 '23 at 22:33
  • 1
    @OldProgrammer: That's a useful thing for anyone with questions about binary floating point to read, but it's by no means a duplicate. The errors it's talking about don't even occur for integer values divided by `2` in any event. – ShadowRanger Mar 28 '23 at 22:54

1 Answers1

3

There's two parts of your question:

  1. Will every representable integer value divided by 2 divide to precisely to a multiple of .5?
  2. Will Math.round round portably?

The answer to both should be yes (though obviously implementers could do some strange stuff). Specifically:

  • IEE-754 binary floating point values (which JS's Number is implemented in terms of, specifically the binary64 form) use a scaling factor based on a power-of-2; as such, any integer value representable by Number can be divided by 2 without precision loss; either it divides evenly (and no scaling factor is needed), or it's odd, and the scaling factor is used to store it internally as basically "original value divided by 2**1".

  • Math.round is required by the spec to round towards positive infinity:

    If the fractional portion is exactly 0.5, the argument is rounded to the next integer in the direction of +∞.

Between those two guarantees, you don't need to worry; 7/2 will always be 3.5 precisely, and Math.round(7/2) will always be 4. It's only when you divide by non-powers-of-2 (or huge powers-of-2), or you're dividing something that's not a precise integer, that error can creep in.

If you're doing something that produces imprecise results in IEEE-754 binary floating point, it'll be imprecise, but it should be portably imprecise; JS is not C, the spec explicitly requires a double-precision 64-bit binary format IEEE 754, not "whatever the processor happens to provide", so you don't have to worry about esoteric hardware that provides floating point that's incompatible with IEEE-754 (if the hardware doesn't support it, any compliant JS engine would need to implement it in software).

ShadowRanger
  • 143,180
  • 12
  • 188
  • 271
  • Wait so I might have to worry about something like 9/6? I'm actually doing someRandomInt/n where n goes cycles from 2 through 9. So 6 is the only n that isn't a power of 2 and could give x.5 – Curtis Mar 29 '23 at 02:13
  • @Curtis: I believe if it's evenly divisible by everything *but* the power of 2 factors, you should be okay. So `9 / 6` cancels out a `3`, leaving `3 / 2`, which is lossless. The issue with non-power-of-2 divisors occurs when they trigger approximations of values that can't be represented in base-2. So if you arrived at `9` by way of, say `9 / 27 * 9 * 3` (which is logically just `9`), the first step will be imprecise, and therefore the result may not actually be `9` (I haven't checked). But if it's a literal integer value `9` divided by `6`, you're in the clear. – ShadowRanger Mar 29 '23 at 10:42
  • Re “imprecise results”: The issue is accuracy (the faithfulness with which values are obtained) not precision (the fineness with which values are represented). All normal numbers in IEEE-754 have the same precision. – Eric Postpischil Mar 29 '23 at 11:46