Open teadrinker opened 4 months ago
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! :)
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 float
s 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 long
s. I will consider it for 32-bit integers.
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!)
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
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!
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()
.
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)
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.
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.
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...
Integer literals are promoted to float
, not to double
. So, with the current Git fut, this works with all float
s:
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));
}
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#)
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#)
(https://github.com/fusionlanguage/fut/issues/83 seem to be a related issue)