Here is my attempt to summarize what we have covered so far in this discussion:

In trunk today, both sin and cos are implemented in terms of #primitiveSine, installed as <primitive: 56> and accessed by Float>>sin. There is no #primitiveCosine.

The lack of #primitiveCosine in the interpreter plugins is the source of the unexpected behavior reported in the beginning of this thread, in which #sin produced plausible looking (but not meaningfully correct) results for sin of large values, but #cos produced inconsistent results. Neither sin or cos was meaningfully correct, but the cos result is inconsistent and confusing.

We also have the available FloatMathPlugin, which supports many math functions including #primitiveSin and #primitiveCos. These are not currently used by the trunk image.

In general terms, FloatMathPlugin favors correctness over performance. In this context, "correctness" refers to correct float rounding behavior (see Nicolas' earlier references).

If we want to fix the inconsistency as originally reported in this thread, then a simple solution is to implement #primitiveCosine in the interpreter.

If we want a more generally correct implementation, then we should use the FloatMathPlugin primitives (presumably with fallback to the earlier implementations).

There may be legitimate tradeoffs between performance and correctness here. As far as I know, no one has yet measured the performance impact of using FloatMathPlugin for sin and cos in trunk.

I am not an expert here, just trying to summarize the discussion so please correct me where I have this wrong.

Dave


On 2024-05-01 09:33, Nicolas Cellier wrote:

Moreover,
I recommend reading this advocacy for providing correctly rounded library for a set of mathematical functions:
https://hal.science/hal-04474530/document
 
Even if accuracy sounds useless as pointed out by Vanessa, this analysis was carried in a certain context.
But maybe it's vital to deliver accurate cos(10**22) for implementing a multi-precision library, who knows,
we just can't guess in which context a general purpose computing environment such as Squeak will be used.
So if a state of the art implementation exists, then we'd better integrate it.
 
I personally wanted to replace fdlibm used by FloatMathPlugin with CRLibm, but was stopped by the uncertain maintenance status...
 
Nicolas

Le mer. 1 mai 2024 à 11:44, Nicolas Cellier <nicolas.cellier.aka.nice@gmail.com> a écrit :
Vanessa is perfectly right.
However, the accuracy of our cos implementation starts to degrade sooner than necessary.
 
If I compare with ArbitraryPrecisionFloat, which has a faithfully rounded implementation,
the sine tend to agree, at least on my Mac:
 
    #(1 10 100 1000 10000)
         collect: [:f | ((f asArbitraryPrecisionFloatNumBits: 53) sin - f sin) asFloat].
    ->  #(0.0 0.0 0.0 0.0 0.0)
 
For the cosine, see how the accuracy is degrading:
 
    #(1 10 100 1000 10000)
         collect: [:f | ((f asArbitraryPrecisionFloatNumBits: 53) cos - f cos) asFloat].
    ->  #(0.0 0.0 2.6645352591003757e-15 4.2521541843143495e-14 2.274846977456946e-13)
 
Our naive implementation of cos prevents us to benefit from the efforts and knowledge put in the libm...
 
I would recommend using the FloatMathPlugin in trunk.
Bonus: we would get reproducible Float operations across OSes.
For the time being, this is not the case, our primitives rely on various libm implementations provided by the OS.
So the sine will deliver different results on Windows MacOS and Linux for example...
 
Nicolas

Le mer. 1 mai 2024 à 01:40, Vanessa Freudenberg <vanessa@codefrau.net> a écrit :
10**22
At that magnitude, each float is more than 1,000,000 apart:
So there's MANY MANY multiples of pi in between successive floats. Asking the sine or cosine of that is nonsensical. 
 
Vanessa
 

On Tue, Apr 30, 2024 at 14:24 <lewis@mail.msen.com> wrote:

On 2024-04-30 10:45, dgray@iesl.forth.gr wrote:

When I start messing about with a new language I have a few corner cases I try out just to see what answers I expect.
For Squeak this has been OK except for cos.
The two numbers I check are totally away from those I routinely use but are stress tests.
(10**22) sin. -0.8522008497671888  "which is correct"
(10**22) cos. -0.8522008497671888 "gives the same as sin which is suspicious"
(2**120) sin.  0.377820109360752 "Also correct"
(2**120) cos. 0.377820109360752 "Also the same as the sin case. I expect -0.9258790228548379"
_______________________________________________
Beginners mailing list -- beginners@lists.squeakfoundation.org
To unsubscribe send an email to beginners-leave@lists.squeakfoundation.org

 

Hello and welcome to Squeak! Thank you very much for reporting this, and congratulations on a very nice bug catch.

I think that you have identified an issue related to floating point data conversions. I am copying the main squeak-dev list on this, where I anticipate some lively follow up discussion.

For follow up on squeak-dev: The cosine logic for a large integer involves first converting the integer to a Float, then sending Float>>cos, which answers (self + Halfpi) sin. If the receiver is a very large floating point value, then self + Halfpi is the same as self, and we effectively calculate sin where cos was expected. At least I think that's what's happening, I'm sure someone will correct me quickly if I have it wrong :-)

Dave