Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

improvements to float32 support needed #172

Open
teadrinker opened this issue Jul 30, 2024 · 10 comments
Open

improvements to float32 support needed #172

teadrinker opened this issue Jul 30, 2024 · 10 comments
Assignees
Labels
bug Something isn't working

Comments

@teadrinker
Copy link

There seems to be no way to define a float inline (such as 1f or 1.f)
There is also no way to do type cast as far as I can tell (if there is, please add to documentation)
It's also a bit unclear how the auto-casting behaves, if you assign to a float, it always seem to make sure type is correct, but if you pass a float to Math.Sin, it does not (this actually causes compile error on c#)

float a = 1.0;
float b = Math.Sin(a);

However, For C/C#, ideally you want sinf/MathF if you pass a float to sin, and not to have it converted to double...
I guess it works like that automatically in C++ already since std::sin has both versions...
(I would also be fine with a separate prefix, so the fusion code would be explicit in which version you call, just like in c#)

(#83 seem to be a related issue)

@teadrinker
Copy link
Author

I saw casting was mentioned for integers using Math.Trunc (however, this function seem to have been renamed Truncate now)

It feels a bit weird that it currently maps to js/ts trunc which is more like floor in the way that it supports values outside the integer range, I feel it would be more consistent to map it to (x) => x|0

Anyway, glad to find this cool project!
Super impressive to support all those container types, congrats! :)

@pfusik pfusik added the bug Something isn't working label Jul 30, 2024
@pfusik pfusik self-assigned this Jul 30, 2024
@pfusik
Copy link
Collaborator

pfusik commented Jul 30, 2024

Thank you for kind words!

There are no float literals. Every float literal can be precisely expressed as a double literal. JavaScript doesn't have floats at all, so you shouldn't rely on the decreased precision. Use float solely to conserve memory.

There are no number cast operators. If you want a number of a different type, assign it to a new variable.
float b = Math.Sin(a); is the correct syntax. I will fix the bug of translating it incorrectly. I will also emit the float math functions.

The result of Math.Truncate can be assigned to an integer. Thanks for spotting a doc typo.
x | 0 is limited to 32-bit range, i.e. can't be used for longs. I will consider it for 32-bit integers.

@teadrinker
Copy link
Author

Use float solely to conserve memory.

I see, I was thinking about cases like audio and graphics (using float for better performance)
But I can see how encouraging float32 use would make the result less consistent between platforms...
You can still do it of course, it's just the the code becomes longer/annoying when every constant need to have it's own variable in order not to use double.

I will consider it for 32-bit integers.

Sorry, yes, I realize now it is only relevant for the 32-bit case.
(I thought python trunc returned an int32, however, it seems "int" is 64bit in python these days!)

@pfusik
Copy link
Collaborator

pfusik commented Jul 30, 2024

Indeed, for heavy calculations, float vs double makes a difference.

I intended the Math functions to be overloaded:

double d = Math.Sin(doubleAngle);
float f = Math.Sin(floatAngle);

The above are unambiguous and it's possible to transpile the second one to MathF.
But what about:

double x = Math.Sin(floatAngle); // Math.Sin or MathF.Sin?
float y = 5 * Math.Sin(floatAngle); // float multiplication or double multiplication?

Now I think that adopting .NET's explicit MathF might be a good idea.

(I thought python trunc returned an int32, however, it seems "int" is 64bit in python these days!)

Not sure what you're referring to, but Python has bignums:

C:\0>python
Python 3.11.9 (main, Apr 12 2024, 09:55:31)  [GCC 13.2.0 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> int(1 << 2000)
114813069527425452423283320117768198402231770208869520047764273682576626139237031385665948631650626991844596463898746277344711896086305533142593135616665318539129989145312280000688779148240044871428926990063486244781615463646388363947317026040466353970904996558162398808944629605623311649536164221970332681344168908984458505602379484807914058900934776500429002716706625830522008132236281291761267883317206598995396418127021779858404042159853183251540889433902091920554957783589672039160081957216630582755380425583726015528348786419432054508915275783882625175435528800822842770817965453762184851149029376
>>> import math
>>> math.trunc(1 << 2000)
114813069527425452423283320117768198402231770208869520047764273682576626139237031385665948631650626991844596463898746277344711896086305533142593135616665318539129989145312280000688779148240044871428926990063486244781615463646388363947317026040466353970904996558162398808944629605623311649536164221970332681344168908984458505602379484807914058900934776500429002716706625830522008132236281291761267883317206598995396418127021779858404042159853183251540889433902091920554957783589672039160081957216630582755380425583726015528348786419432054508915275783882625175435528800822842770817965453762184851149029376

@teadrinker
Copy link
Author

teadrinker commented Jul 30, 2024

Nice, being explicit is probably the best way, will be easier to reason about the code.

Not sure what you're referring to, but Python has bignums

wow, that's sweet! I knew it had bignums, I never realized it's THE native int type!
Guess porting/running hashing functions is not ideal then, but it's a pretty epic feat that range of native integers are only limited by memory!

@pfusik
Copy link
Collaborator

pfusik commented Jul 30, 2024

I keep thinking of the API design.

I expect the calculations to consistently use double for precision, or float for efficiency. No language provides functions with mixed parameter/result precisions. Hence:

double x = Math.Sin(floatAngle); // MathF.Sin
float y = 5 * Math.Sin(floatAngle); // float multiplication

In rare cases where precision needs to be mixed, intermediate variables can be introduced.

I'm going to fully read the MathF story, but at first it looks the reasons were backward compatibility (Math.Sin(2.0f) yielded a double in the already-popular .NET). MathF.Abs/Min/Max are duplicates of the corresponding Math methods. Not a good design. Especially if we later add half or whatever-new-fp-type.

.NET 7 adds float.Sin(foo) etc. Some languages go even further: foo.Sin().

@teadrinker
Copy link
Author

I agree that we can assume mixed precision is not a common/important case

MathF is a pretty ugly prefix, Unity game engine has its own Mathf as well, so there is already at least 3 in Unity...

float.Sin(foo)

hmm, never saw that, but quite nice, possibly the best so far?

foo.Sin()

hmm, this would take some time to get use to, sin with empty parens, weird...
I feel in many cases you'd get something like (a*b+c).Sin() anyway, which is just more complicated...

My personal preference is like C-style global sin(), but accepting multiple types like hlsl/glsl.
However I think this is ruled out in this context due to naming conventions, that might also have
issues with ambiguity (might still require something like float.Sin()/MathF.Sin() as fallback)

pfusik added a commit that referenced this issue Aug 2, 2024
@pfusik
Copy link
Collaborator

pfusik commented Aug 2, 2024

I decided to add float overloads. float b = Math.Sin(a); works now, emitting sinf, MathF.Sin, Float sin overload in Swift, (float) Math.sin in Java.

Being explicit about the type is not how computations generally work in high-level languages. We are used to overloaded arithmetic operators. I see no reason to spell MathF or float for built-in math functions. And I agree the method syntax would be bizarre.

Not closing this ticket yet, as tests need to be added and Abs/Clamp/Max/Min need to be handled.

@teadrinker
Copy link
Author

teadrinker commented Aug 2, 2024

Being explicit about the type is not how computations generally work in high-level languages.
We are used to overloaded arithmetic operators. I see no reason to spell MathF or float for built-in math functions.

I agree it's much more elegant if this can be solved using overloading!

Let me explain more about the context why I would like to see float literals supported...
One of the things I've been doing somewhat regularly for 15 years is prototyping sound synthesis/processing
inside js
. The result is then manually converted to C/C++, or similar...

So the use case here is not really high level, nor a complete app, I would run fusion to emit js during prototyping, and then export a high performant C.

Example:

public double sweep(double x)
{
    double sweep = (1.0544 - Math.Cos(x / 4));
    return Math.Sin(42 * sweep * (1 + (3 + Math.Sin(x * 350)) / 8));
}
public float sweepf(float x)
{
    float f1 = 1.0;
    float f3 = 3.0;
    float f4 = 4.0;
    float f8 = 8.0;
    float f42 = 42.0;
    float f350 = 350.0;
    float c = 1.0544;
    float sweep = (c - Math.Cos(x / f4));
    return Math.Sin(f42 * sweep * (f1 + (f3 + Math.Sin(x * f350)) / f8));
}

so yeah, a very extreme example, and a very fringe use case in the grand scheme of things... So you might be better off ignoring this unless these are easy fixes...

An alternative approach that would work for this use case, is some kind of way to define the "default number precision", feels a bit more high level...

@pfusik
Copy link
Collaborator

pfusik commented Aug 10, 2024

Integer literals are promoted to float, not to double. So, with the current Git fut, this works with all floats:

public static float Sweep(float x)
{
    float offset = 1.0544;
    float sweep = offset - Math.Cos(x / 4);
    return Math.Sin(42 * sweep * (1 + (3 + Math.Sin(x * 350)) / 8));
}

pfusik added a commit that referenced this issue Aug 12, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

2 participants