Hacker News new | past | comments | ask | show | jobs | submit login

Not in Raku it doesn't!

    > 1.1 + 2.2
    3.3
    > 1.1 + 2.2 == 3.3
    True
EDIT: to be clear: this is not because Raku is magic, it's because Raku defaults to a rational number type for decimal literals, which is arguably a much better choice for a language like Raku.



The same goes in Common Lisp, but for very different reasons:

  * (= (+ 0.1 0.2) 0.3)
  T
In Common Lisp, there is a small epsilon used in floating-point equality: single-float-epsilon. When two numbers are within that delta, they are considered equal.

Meanwhile, in Rakudo, 0.1 is a Rat: a rational number where the numerator and denominator are computed.

You can actually get the same underlying behavior in Common Lisp:

  (= (+ 1/10 2/10) 3/10)
Sadly, not many recent languages have defaults as nice as those. Another example is Julia:

  julia> 1//10 + 2//10 == 3//10
  true
IMO, numerical computations should be correct by default, and fast in opt-in.


Ahem, this is about 0.1 + 0.2. I think Raku also uses IEEE 754 double precision floating point numbers for the Num type, no?

Edit: it seems that Raku uses rationals as a default [1], so it doesn't suffer from the same problem by default.

[1]: https://0.30000000000000004.com/#raku


Oh, missed that, it's usually 1.1 + 2.2 in these kinds of discussions.

Yeah, exactly, Raku defaults to a rational number type for these kinds of numbers. I honestly think that is a perfectly fine way to do it, you're not using Raku for high performance stuff anyway. It's not so different from how Python will start to use arbitrarily sized integers if it feels it needs to.

Raku by default will convert it to a float if the denominator gets larger than a 64-bit int, but there's actually a current pull request active that lets you customize that behavior to always keep it as a Rat.

Really interesting language, Raku!


Because that syntax in raku uses rational type, which fails for many other uses, and by using the syntax most languages use for a floating type, makes it harder to spot these issues, just like here. For example,

    0.1e0 + 0.2e0 
yields 0.30000000000000004. Your example also fails

    1.1e0 + 2.2e0 == 3.3e0
returns false.


I mean, yeah, if you force the numbers to be floats, then of course it's going to fail. I personally think Raku's way of defaulting to floats is the better way to go for a scripting language like this, and I disagree that "it fails for many other uses". It works just fine (like, it doesn't break if you pass it to sqrt() or whatever), it's just less performant. It's the exact same kind of tradeoff that Python's implicit promotion to big integers make.


>I disagree that "it fails for many other uses"

Having developed a significant amount of perfect precision math libs over the years, rationals do explode for lots of common computations. That is the main reason they are not standard in all computing. They also cannot represent a lot of desired results.

The problem is rational number performance slows exponentially (or uses large amounts of RAM) for many common uses, which will kill scripts, unless they suddenly fix precision (i.e., no longer exact) or change to float (also a surprise for people).

Setting them as floats has the odd numerical issue, which is not that bad, but doesn't require a host of other mitigations to prevent other bad surprises.

For example, summing 1/n^2 for n=1 to 100000 as floats runs quickly and is very close to the exact answer. As rationals the numerator and denominator require working with 86000 digit numbers.

Also, does raku still do this?

say (1/6+1/6).raku #<1/3>

say (1/10+1/10).raku #0.2

That seems surprising, does it not?


Yes, that’s precisely what I am trying to say. With FatRat (aka BigInt) by default then the casual programmer has to know to step explicitly into Num (eg by 1e-1 + 2e-1 which needs a bit of a scientist mindset) or will never benefit from all those lovely FPU transistors.


say (1/10+1/10).WHAT; say (1/6+1/6).WHAT;

both are Rats, they just get stringified differently


They stringify differently because 2/10 can be expressed exactly in NNN.MMM format, whereas 1/3 can not.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: