Skip to content

Commit

Permalink
Fix issue with casts from floating point to integer not truncating
Browse files Browse the repository at this point in the history
- Fix issue where casting floats to integers was not handled properly so it would not truncate floats and instead was rounding them.
  • Loading branch information
MerlinVR committed Apr 25, 2020
1 parent a2a399d commit 2cf9088
Show file tree
Hide file tree
Showing 2 changed files with 48 additions and 1 deletion.
20 changes: 19 additions & 1 deletion Assets/UdonSharp/Editor/UdonSharpExpressionCapture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -524,10 +524,28 @@ public SymbolDefinition CastSymbolToType(SymbolDefinition sourceSymbol, System.T

if (conversionFunction != null && (isExplicit || isNumericCastValid))
{
SymbolDefinition sourceNumericSymbol = sourceSymbol;

// System.Convert.ToIntXX with a floating point argument will not be truncated, instead it will be rounded using Banker's Rounding.
// This is not what we want for casts, so we first floor the input before running the conversion
if (UdonSharpUtils.IsFloatType(sourceSymbol.symbolCsType) && UdonSharpUtils.IsIntegerType(targetType))
{
// Mathf.Floor only works on floats so if it's a double we need to convert it first.
// This does lose a small amount of accuracy on gigantic numbers, but it should hopefully be enough until Udon has dedicated cast instructions at some point in the future
SymbolDefinition inputFloat = CastSymbolToType(sourceSymbol, typeof(float), true);
conversionFunction = UdonSharpUtils.GetNumericConversionMethod(targetType, typeof(float));

using (ExpressionCaptureScope floatFloorMethodCaptureScope = new ExpressionCaptureScope(visitorContext, null))
{
floatFloorMethodCaptureScope.SetToMethods(new[] { typeof(Mathf).GetMethod("Floor", BindingFlags.Static | BindingFlags.Public) });
sourceNumericSymbol = floatFloorMethodCaptureScope.Invoke(new SymbolDefinition[] { inputFloat });
}
}

// This code is copied 3 times, todo: find a decent way to refactor it
SymbolDefinition castOutput = visitorContext.topTable.CreateUnnamedSymbol(targetType, SymbolDeclTypeFlags.Internal);

visitorContext.uasmBuilder.AddPush(sourceSymbol);
visitorContext.uasmBuilder.AddPush(sourceNumericSymbol);
visitorContext.uasmBuilder.AddPush(castOutput);
visitorContext.uasmBuilder.AddExternCall(visitorContext.resolverContext.GetUdonMethodName(conversionFunction));

Expand Down
29 changes: 29 additions & 0 deletions Assets/UdonSharp/Editor/UdonSharpUtils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,25 @@ public static bool HasModifier(this SyntaxTokenList syntaxTokens, string modifie
typeof(long),
};

private static readonly HashSet<System.Type> integerTypes = new HashSet<System.Type>()
{
typeof(byte),
typeof(sbyte),
typeof(short),
typeof(ushort),
typeof(int),
typeof(uint),
typeof(long),
typeof(ulong),
};

private static readonly HashSet<System.Type> floatTypes = new HashSet<System.Type>()
{
typeof(float),
typeof(double),
typeof(decimal),
};

public static bool IsSignedType(System.Type type)
{
return signedTypes.Contains(type);
Expand All @@ -113,6 +132,16 @@ public static bool IsUnsignedType(System.Type type)
return unsignedTypes.Contains(type);
}

public static bool IsIntegerType(System.Type type)
{
return integerTypes.Contains(type);
}

public static bool IsFloatType(System.Type type)
{
return floatTypes.Contains(type);
}

public static bool IsNumericType(System.Type type)
{
return implicitBuiltinConversions.ContainsKey(type);
Expand Down

0 comments on commit 2cf9088

Please sign in to comment.