i_float 4.0.0

This fixed float math library provides an efficient and deterministic solution for arithmetic and geometric operations.
Documentation

iFloat

i_float provides numeric primitives for deterministic 2D geometry:

  • generic integer points, vectors, and rectangles;
  • wide intermediate integer arithmetic;
  • conversion between floating-point and integer coordinate spaces;
  • fixed-scale unit ratios for interpolation;
  • basic triangle predicates;
  • optional serde and glam integration.

The crate is no_std and supports i16, i32, and i64 coordinate types.

Installation

[dependencies]
i_float = "4.0"

The default core feature exposes the complete numeric and geometry API.

Integer geometry

IntPoint<T> stores coordinates in T. Subtracting two points produces an IntVector<T> whose components use the associated wide integer type: i32 coordinates produce an i64 vector, while i64 coordinates produce an i128 vector.

use i_float::int::point::IntPoint;
use i_float::triangle::Triangle;

let a = IntPoint::new(0_i32, 0);
let b = IntPoint::new(10, 0);
let c = IntPoint::new(0, 10);

let ab = b - a;
assert_eq!(ab.x, 10_i64);
assert_eq!(ab.y, 0_i64);

assert_eq!(Triangle::area_two(a, b, c), 100_i64);
assert!(!Triangle::is_clockwise(a, b, c));

Coordinate range

Integer geometry intentionally uses a coordinate range narrower than the full range of the underlying integer. Although point differences are widened, dot products, cross products, and squared lengths multiply wide values without widening them again.

A conservative common bound for all point and vector operations is:

-2^(I::BITS - 2) < coordinate < 2^(I::BITS - 2)

For example, i32 coordinates should stay strictly between -1_073_741_824 and 1_073_741_824. This leaves enough headroom for the difference of two points and for the sum or difference of two products in I::Wide. Operations use normal integer arithmetic and do not perform runtime range checks.

This bound is deliberately universal and conservative. An algorithm may use a wider range when it proves that its particular intermediate expressions still fit. Conversely, an IntVector constructed directly from arbitrary wide values is not covered by the point-coordinate bound.

Floating-point input should normally be mapped with FloatPointAdapter. For an explicit general-purpose safety margin, use with_coordinate_bits with at most I::BITS - 3; algorithms with stronger range analysis may select a larger bit budget.

Floating-point adapter

FloatPointAdapter maps a bounded floating-point coordinate space onto an integer grid. The same adapter converts results back into the original space.

use i_float::adapter::FloatPointAdapter;
use i_float::float::rect::FloatRect;
use i_float::int::point::IntPoint;

let bounds = FloatRect::new(-10.0_f64, 10.0, -5.0, 5.0);
let adapter = FloatPointAdapter::<[f64; 2], i32>::new(bounds);

let source = [2.5, -1.25];
let point: IntPoint<i32> = adapter.try_float_to_int(&source).unwrap();
let restored = adapter.try_int_to_float(&point).unwrap();

let tolerance = adapter.inv_scale();
assert!((restored[0] - source[0]).abs() <= tolerance);
assert!((restored[1] - source[1]).abs() <= tolerance);

Use with_coordinate_bits when an algorithm has an explicit coordinate-bit budget. The value controls only the converted coordinate magnitude; it does not prove that every later arithmetic expression is safe. Use try_with_scale or try_with_scale_and_coordinate_bits when a caller supplies the scale and invalid or unsafe scales must be rejected.

Fixed-scale ratios

UnitRatio<I> represents a value in the inclusive range 0..=1. Its stored integer value uses FixedScale<I>::DENOMINATOR as one. Scaling rounds midpoint values away from zero.

use i_float::int::number::unit_ratio::UnitRatio;
use i_float::int::point::IntPoint;

let quarter = UnitRatio::<i32>::from_int(1, 4);
let half = UnitRatio::<i32>::half();

assert_eq!(quarter.scale(10), 3);
assert_eq!(quarter.scale(-10), -3);
assert_eq!(quarter.mid(half), UnitRatio::from_int(3, 8));

let point = IntPoint::new(100, -40);
assert_eq!(quarter.scale_point(point), IntPoint::new(25, -10));

Constructors currently expect valid input. In particular, new expects a stored value between zero and DENOMINATOR, from_float expects a finite value between zero and one, and from_int expects 0 <= numerator <= denominator. These preconditions are checked by debug assertions.

Features

Feature Default Description
core yes Integer and floating-point primitives, adapters, and triangle predicates
serde no Enables serialization for supported geometry types and also enables core
glam no Adds conversions for glam::Vec2, DVec2, and IVec2 and also enables core

License

Licensed under the MIT License. See the LICENSE file.