Jeff Atwood Answers and Square Roots



  • Okay, yes. Now you try this:

    #include <math.h>
    #include <assert.h>
    #include <stdio.h>
    int main (int argc, char ** argv) {
      float fx = 0.3;
      printf ("float %f %08x\n", fx, *((unsigned int*)&fx));
      fx -= 0.2;
      printf ("float %f %08x\n", fx, *((unsigned int*)&fx));
      fx -= 0.1;
      printf ("float %f %08x\n", fx, *((unsigned int*)&fx));
      assert(fx == 0.0);
      return 0;
    }
    

    So sqrt(4) - 2 is exactly 0, but 0.3 - 0.2 - 0.1 is not. In Windows calculator, it's the other way round (as evidenced by taking the reciprocal of both values):

    So, if you can't have it both ways, which would you prefer in your calculator: 0.3 - 0.2 - 0.1 results in exactly 0, or sqrt(4) - 2 does? Keep in mind, the average person is probably going to use subtraction a lot more often than square root.



  • @anotherusername said:

    So, if you can't have it both ways, which would you prefer in your calculator:

    You can have it both ways, as long as you don't restrict yourself to floating point calculations. So my reply to that would be "I would require both", as you can consider anything that doesn't provide both as being untrustworthy and / or useless.

    The bug in Windows number-copulator is evidence of a failure to get "having it both ways" right. It's a bug. It's not a floating point issue, except in that there's conversion to or from from floating point and some internal (probably) rational representation that's going wrong, almost certainly where they go to floating point in order to use the inbuilt square root, and the result of that is above epsilon.



  • @tufty said:

    The result of fsqrt, the intel floating point square root opcode, which has existed since the 8087 coprocessor

    ... is, as previously mentioned, irrelevant because the calculator is not using floats or doubles; it is using a higher precision datatype. It's the square root algorithm in the library that it's using for that datatype that is returning a result which is out by one in the last bit. Which, although it's not ideal, should be understood as the possible error you can get when calling any floating point operation. As @anotherusername pointed out, it's probably not worth it to have a special code path for calculating the square root of a square integer. (And if they did you'd probably just complain about the result of "cube root of 27 minus three" instead.)

    I came across the following last night in a completely different context*, from the [url="https://docs.oracle.com/javase/8/docs/api/java/math/BigDecimal.html"]java BigDecimal documentation[/url]:

    For example, the result of the pow method using the specified algorithm can occasionally differ from the rounded mathematical result by more than one unit in the last place, one ulp.
    So yeah, these things happen when you're doing floating point calculations - though that doesn't mean that the algorithm can't or shouldn't be improved to do it less often. Of course, the cost-benefit ratio on that probably isn't very good.

    * Trying to work out how to get a Java transformation in Informatica to produce an Informatica datetime output** (Informatica maps its datetime datatype to a Java BigDecimal rather than to, for instance, a Date).
    ** Informatica_date_port = new BigDecimal(java_date_expr.getTime());
    But next time I'm probably just going to output the date as a string and parse it in Informatica, it's a lot easier.



  • @Scarlet_Manuka said:

    cube root of 27 minus three

    Have you tried that one?

    I would argue that the square root operation must be giving the right value for its parameter, as sqrt(4) - x is correct for (I believe) everything but 2.

    <goes off to check>



  • Given that it was in the context of a hypothetical calculator with a modified algorithm, no, but since you ask: -3.0657302255428452002343108268391e-38



  • @tufty said:

    sqrt(4) - x is correct for (I believe) everything but 2.

    The point is that a difference of 1 ulp (see above for definition) is not going to be visible in the result unless the true result is 0.



  • Oh, you're right, of course it won't.

    <belm>



  • @anotherusername said:

    sqrt(4.1 - 0.1)

    I don't see why [integer] + [float constant] - [same float constant] would equal anything other than [integer].



  • @anotherusername said:

    So, if you can't have it both ways

    I can totally have it both ways.



  • @anotherusername said:

    It's generally understood that when you're talking about "the square root" you're referring to the principal square root, and it is unique. Otherwise you say "a square root" or plural, "square roots".

    I see. That's what my ESL made me misunderstand.



  • @boomzilla said:

    But you went on to mention "the square root of 0" without specifying what numbers you were using!

    Sorry about that. I was using Arabic numbers 🍹


  • ♿ (Parody)

    @OffByOne said:

    @boomzilla said:
    But you went on to mention "the square root of 0" without specifying what numbers you were using!

    Sorry about that. I was using Arabic numbers 🍹

    Is that a field or a group or a ring or what? I'm not familiar with that one. 🏆



  • @boomzilla said:

    @OffByOne said:
    @boomzilla said:
    But you went on to mention "the square root of 0" without specifying what numbers you were using!

    Sorry about that. I was using Arabic numbers 🍹

    Is that a field or a group or a ring or what? I'm not familiar with that one. 🏆

    If what I hear on the news is true, it's a terrorist group.



  • @boomzilla said:

    @OffByOne said:
    @boomzilla said:
    But you went on to mention "the square root of 0" without specifying what numbers you were using!

    Sorry about that. I was using Arabic numbers 🍹

    Is that a field or a group or a ring or what? I'm not familiar with that one. 🏆

    They're the kind of numbers used in homemade clocks.

    So a ring?



  • @ben_lubar said:

    @anotherusername said:
    sqrt(4.1 - 0.1)

    I don't see why [integer] + [float constant] - [same float constant] would equal anything other than [integer].

    Because of rounding when converting to and from base 2.

    The floating-point number 4.1 cannot be represented as a finite sum of integer powers of 2. In base 2, it's a repeating decimal, 100.00011(0011). And since a single-precision float has a fixed number of significant binary digits (24 significant binary digits), 4.1 has fewer significant digits after the decimal than 0.1, so 4.1 - 0.1 might not exactly equal 4:

     100.000110011001100110011
    - 0.000110011001100110011001100 (leading 0s are not significant)
    = 11.1111111111111111111111101 (truncate to 24 significant digits)

    Instead of the correct result, 100, you get 11.111111111111111111111. The error is less than the floating point number's unit of least precision, so the number is still basically 4.0. But it's not exactly 4.0.



  • @ben_lubar said:

    @anotherusername said:
    So, if you can't have it both ways

    I can totally have it both ways.

    Hey look, I broke it

    And I didn't even have to use sqrt...


  • Java Dev

    @Scarlet_Manuka said:

    cube root of 27 minus three

    Equals 0 in gnome-calculator. So do sqrt(4.1-.1)-2 and .3-.2-.1

    @anotherusername said:

     100.000110011001100110011
    - 0.000110011001100110011001100 (leading 0s are not significant)
    = 11.1111111111111111111111101 (truncate to 24 significant digits)

    Instead of the correct result, 100, you get 11.111111111111111111111. The error is less than the floating point number's unit of least precision, so the number is still basically 4.0. But it's not exactly 4.0.

    Floating-point hardware normally rounds. ...1101 should clearly round up, resulting in 100.


Log in to reply