Hi,
I had to deal with float numbers in one of my Nix related projects and found out that float support in the Nix libraries (both builtin and from nixpkgs) is very limited. Same goes for integers but the support is slightly better than for floats.
For example, there’s no function to get the power of a number, so I had to make my own (getScalingFactor
is a function to get the power of 10, but it could be slightly modified to get the power of any number).
In the end, I came up with a working solution so I thought it could be useful to share it. If anyone feels something could be done differently or be simpler, I’m all ears!
The goal of the function below is to take a float and truncate it at a given number of decimals.
# Truncate a floating-point number to a specified number of decimal places
# This performs truncation (toward zero) rather than rounding to nearest
#
# Examples:
# truncateFloat 1.379 2 → 1.37 (truncates 0.009)
# truncateFloat 3.14159 3 → 3.141 (truncates 0.00059)
# truncateFloat (-2.789) 1 → -2.7 (truncates toward zero)
#
# Type: Float → Int → Float
float: precision:
let
# Locate the position of the decimal separator in a numeric string representation
# Returns the zero-based index of the decimal point character
# Type: String → Int
findDecimalPointIndex =
numericString:
lib.lists.findFirstIndex (character: character == ".") ((builtins.stringLength numericString) - 1) (
lib.stringToCharacters numericString
);
# Calculate the scaling factor (power of 10) for the specified decimal precision
# Returns 10^precision for shifting decimal point during truncation
# Type: Int → Int
getScalingFactor =
precision: builtins.foldl' (accumulator: x: accumulator * x) 1 (builtins.genList (_: 10) precision);
# Reconstruct a floating-point number from its truncated integer representation
# Type: Int → Int → Float
intToFloatAtPrecision =
int: precision:
let
scalingFactor = getScalingFactor precision;
in
((int + (1.0 / scalingFactor)) / scalingFactor) - (1.0 / (scalingFactor * scalingFactor));
# Extract the integer part from a float string
# Performs truncation by discarding fractional component
# Type: String → Int
floatStringToInt =
floatString: lib.toInt (lib.substring 0 (findDecimalPointIndex floatString) floatString);
# Convert float to string representation after scaling by precision factor
# This shifts the decimal point right by decimalPlaces positions
# Example: 1.379 with precision 2 becomes "137.900000"
# Type: Float → Int → String
toStringFloat = float: precision: lib.strings.floatToString (float * (getScalingFactor precision));
in
intToFloatAtPrecision (floatStringToInt (toStringFloat float precision)) precision
Implementation-wise, the process is to shift the decimal point right by a given number (precision
), truncate the result into an integer and shift back the decimal to get the truncated float.
The function could use some more safeguards like validating the inputs with isFloat
and isInt
. It also doesn’t append zeros if the requested precision is greater than the float input.
Note for moderators: I’m not sure where to put this so feel free to move it around if needed!