Skip to content

gdahlm/zig-libmpdec

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

3 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

zig-libmpdec

A Zig port of libmpdec providing arbitrary-precision decimal arithmetic with IEEE 754 semantics, similar to Python's decimal module.

  • Package name: zig_libmpdec
  • Minimum Zig version: 0.16.0

Important Note

While the authors believe this project is mostly functional and may be usable to you, it was primarily designed while testing LLM/LRM security and memory safety checking on zig 0.16 while it was too new to be supported in various local and cloud models.

The code was extracted and modified from a larger private project and modified expressly to fit the above needs.

The code was mostly hand generated but many fixes were guided by LLM suggestions.

The coding style and comment style specifically target the Qwen 3.6 models preferences and limitations, to both guard against unnecessary changes and to ensure that a zed-agent had the highest chances of success. At the time of development Qwen 3.6 was the only local model that we found that wasn’t tripped up with the zig 0.16 changes like std.Io.

This project is intentionally licensed as CC0-1.0 so that if someone wished they could fork and optimize it. But please do not expect optimizations to be pulled into this repo.

Fuzzing is broken!!

The fuzzing code is LLM generated slop and didn't work with zig 0.16 it is being left in to test how quickly models adapt once zig 0.16.1 is released.

The files that are known to be unusable slop are:

  • /src/fuzz.zig
  • /src/fuzz_targets.zig

They were included here so some friends could mess around with them, just dont run build test --fuzz and they won't get in the way.

Quick Start

Using from Another Zig Project

Option 1: Git URL (recommended)

zig fetch --save git+https://github.com/gdahlm/zig-libmpdec.git

This adds the dependency to your build.zig.zon. Then wire it up in your build.zig:

pub fn build(b: *std.Build) void {
    const target = b.standardTargetOptions(.{});
    const optimize = b.standardOptimizeOption(.{});

    // Get the dependency from your zon file
    const libmpdec_dep = b.dependency("zig_libmpdec", .{
        .target = target,
        .optimize = optimize,
    });

    const exe = b.addExecutable(.{
        .name = "myapp",
        .root_module = b.createModule(.{
            .root_source_file = b.path("src/main.zig"),
            .target = target,
            .optimize = optimize,
            .imports = &.{
                .{ .name = "zig_libmpdec", .module = libmpdec_dep.module("zig_libmpdec") },
            },
        }),
    });

    b.installArtifact(exe);

    const run_step = b.step("run", "Run the app");
    run_step.dependOn(&b.addRunArtifact(exe).step);
}

Option 2: Local Path

If you're developing both projects locally, reference it by path in your build.zig.zon:

// In build.zig.zon dependencies table
.dependencies = .{
    .zig_libmpdec = .{
        .path = "../zig-libmpdec",
    },
},

Then use the same b.dependency(...) pattern in your build.zig as shown above.

Building the Library Itself

Clone the repository and run:

git clone https://github.com/gdahlm/zig-libmpdec.git
cd zig-libmpdec

# Build the example executable
zig build

# Run tests
zig build test

# Run the example app
zig build run

Usage

const std = @import("std");
const libmpdec = @import("zig_libmpdec");

/// Helper: format a decimal and return an owned string.
fn fmt(allocator: std.mem.Allocator, d: *const libmpdec.Decimal) ![]u8 {
    return d.format(allocator);
}

pub fn main(init: std.process.Init) !void {
    const arena = init.arena;
    defer arena.deinit();
    const allocator = arena.allocator();

    // Create a decimal context with 28 digits of precision
    var ctx = libmpdec.Context.init(28);

    // Parse decimal strings
    var a = try libmpdec.Decimal.parse(allocator, "1.23456789");
    defer a.deinit();

    var b = try libmpdec.Decimal.parse(allocator, "9.87654321");
    defer b.deinit();

    // Perform arithmetic
    var sum = try libmpdec.decAdd(&ctx, &a, &b);
    defer sum.deinit();

    // Format the result
    std.debug.print("{s}\n", .{try fmt(allocator, &sum)});
    // Output: 11.11111110
}

API Overview

Types

Type Description
Decimal Arbitrary-precision decimal number (sign, coefficient, exponent)
MPInt Arbitrary-precision integer used for coefficients
Context Holds precision, rounding mode, and exponent limits
RoundingMode Enum: round_half_even, round_half_up, round_towards_zero, etc.
DecimalSpecial Enum: normal, zero, nan, sNaN, inf

Arithmetic Functions

All arithmetic functions take a *Context and return a new Decimal:

Function Description
decAdd(ctx, a, b) Addition
decSub(ctx, a, b) Subtraction
decMul(ctx, a, b) Multiplication
decDiv(ctx, a, b) Division
decNeg(ctx, a) Negation
decAbs(ctx, a) Absolute value
decCopy(ctx, a) Copy a decimal
decCmp(a, b) Comparison (returns std.math.Order)

Decimal Methods

Method Description
Decimal.parse(alloc, str) Parse from string (e.g. "1.23e-4", "NaN", "Infinity")
Decimal.fromInt(n) Create from integer
Decimal.fromUInt(n) Create from unsigned integer
Decimal.nan() / Decimal.inf() Create special values
dec.isZero() / dec.isInfinite() / dec.isNaN() Predicate checks
dec.deinit() Free allocated resources
dec.format(buf, options) Format to string buffer

Memory Management

Decimal.parse allocates memory for the coefficient. Always call deinit() on Decimal instances when you're done with them. The returned Decimal from arithmetic operations owns its own allocation and must also be deinitialized.

Using an ArenaAllocator is recommended when performing many operations, as it simplifies cleanup:

var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
defer arena.deinit();

var a = try libmpdec.Decimal.parse(arena.allocator(), "10");
// No need to individually deinit - arena cleans up everything

Rounding Modes

The Context supports eight IEEE 754 rounding modes:

  • round_half_even (default, "banker's rounding")
  • round_half_up
  • round_half_down
  • round_towards_zero
  • round_away_from_zero
  • round_towards_plus_inf
  • round_towards_minus_inf
  • round_to_odd

Set it when creating the context:

var ctx = libmpdec.Context.init(28);
ctx.rounding = libmpdec.RoundingMode.round_half_up;

Running Tests

zig build test

License

CC0-1.0

About

A Zig port of libmpdec providing arbitrary-precision decimal arithmetic with IEEE 754 semantics, similar to Python's `decimal` module.

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages