Commit d1e68c3ca8

Andrew Kelley <superjoe30@gmail.com>
2017-06-26 20:41:47
better bigint/bigfloat implementation
1 parent 3e8af78
doc/langref.md
@@ -143,7 +143,7 @@ StructLiteralField = "." Symbol "=" Expression
 
 PrefixOp = "!" | "-" | "~" | "*" | ("&" option("const") option("volatile")) | "?" | "%" | "%%" | "??" | "-%"
 
-PrimaryExpression = Number | String | CharLiteral | KeywordLiteral | GroupedExpression | GotoExpression | BlockExpression(BlockOrExpression) | Symbol | ("@" Symbol FnCallExpression) | ArrayType | (option("extern") FnProto) | AsmExpression | ("error" "." Symbol) | ContainerDecl
+PrimaryExpression = Integer | Float | String | CharLiteral | KeywordLiteral | GroupedExpression | GotoExpression | BlockExpression(BlockOrExpression) | Symbol | ("@" Symbol FnCallExpression) | ArrayType | (option("extern") FnProto) | AsmExpression | ("error" "." Symbol) | ContainerDecl
 
 ArrayType = "[" option(Expression) "]" option("const") TypeExpr
 
src/all_types.hpp
@@ -13,7 +13,8 @@
 #include "zig_llvm.hpp"
 #include "hash_map.hpp"
 #include "errmsg.hpp"
-#include "bignum.hpp"
+#include "bigint.hpp"
+#include "bigfloat.hpp"
 #include "target.hpp"
 
 struct AstNode;
@@ -215,6 +216,11 @@ struct ConstGlobalRefs {
     LLVMValueRef llvm_global;
 };
 
+enum ConstNumLitKind {
+    ConstNumLitKindInt,
+    ConstNumLitKindFloat,
+};
+
 struct ConstExprValue {
     TypeTableEntry *type;
     ConstValSpecial special;
@@ -222,7 +228,8 @@ struct ConstExprValue {
 
     union {
         // populated if special == ConstValSpecialStatic
-        BigNum x_bignum;
+        BigInt x_bigint;
+        BigFloat x_bigfloat;
         bool x_bool;
         ConstFn x_fn;
         ConstBoundFnValue x_bound_fn;
@@ -347,7 +354,8 @@ enum NodeType {
     NodeTypeTestDecl,
     NodeTypeBinOpExpr,
     NodeTypeUnwrapErrorExpr,
-    NodeTypeNumberLiteral,
+    NodeTypeFloatLiteral,
+    NodeTypeIntLiteral,
     NodeTypeStringLiteral,
     NodeTypeCharLiteral,
     NodeTypeSymbol,
@@ -748,14 +756,18 @@ struct AstNodeCharLiteral {
     uint8_t value;
 };
 
-struct AstNodeNumberLiteral {
-    BigNum *bignum;
+struct AstNodeFloatLiteral {
+    BigFloat *bigfloat;
 
     // overflow is true if when parsing the number, we discovered it would not
-    // fit without losing data in a uint64_t or double
+    // fit without losing data in a double
     bool overflow;
 };
 
+struct AstNodeIntLiteral {
+    BigInt *bigint;
+};
+
 struct AstNodeStructValueField {
     Buf *name;
     AstNode *expr;
@@ -854,7 +866,8 @@ struct AstNode {
         AstNodeStructField struct_field;
         AstNodeStringLiteral string_literal;
         AstNodeCharLiteral char_literal;
-        AstNodeNumberLiteral number_literal;
+        AstNodeFloatLiteral float_literal;
+        AstNodeIntLiteral int_literal;
         AstNodeContainerInitExpr container_init_expr;
         AstNodeStructValueField struct_val_field;
         AstNodeNullLiteral null_literal;
src/analyze.cpp
@@ -2194,7 +2194,8 @@ void scan_decls(CodeGen *g, ScopeDecls *decls_scope, AstNode *node) {
         case NodeTypeFnCallExpr:
         case NodeTypeArrayAccessExpr:
         case NodeTypeSliceExpr:
-        case NodeTypeNumberLiteral:
+        case NodeTypeFloatLiteral:
+        case NodeTypeIntLiteral:
         case NodeTypeStringLiteral:
         case NodeTypeCharLiteral:
         case NodeTypeBoolLiteral:
@@ -3247,10 +3248,17 @@ static uint32_t hash_const_val(ConstExprValue *const_val) {
         case TypeTableEntryIdInt:
         case TypeTableEntryIdNumLitInt:
         case TypeTableEntryIdEnumTag:
-            return ((uint32_t)(bignum_to_twos_complement(&const_val->data.x_bignum) % UINT32_MAX)) * (uint32_t)1331471175;
+            {
+                uint32_t result = 1331471175;
+                for (size_t i = 0; i < const_val->data.x_bigint.digit_count; i += 1) {
+                    uint64_t digit = bigint_ptr(&const_val->data.x_bigint)[i];
+                    result ^= ((uint32_t)(digit >> 32)) ^ (uint32_t)(result);
+                }
+                return result;
+            }
         case TypeTableEntryIdFloat:
         case TypeTableEntryIdNumLitFloat:
-            return (uint32_t)(const_val->data.x_bignum.data.x_float * (uint32_t)UINT32_MAX);
+            return (uint32_t)(const_val->data.x_bigfloat.value * (uint32_t)UINT32_MAX);
         case TypeTableEntryIdArgTuple:
             return (uint32_t)const_val->data.x_arg_tuple.start_index * (uint32_t)281907309 +
                 (uint32_t)const_val->data.x_arg_tuple.end_index * (uint32_t)2290442768;
@@ -3473,7 +3481,7 @@ void init_const_str_lit(CodeGen *g, ConstExprValue *const_val, Buf *str) {
         ConstExprValue *this_char = &const_val->data.x_array.s_none.elements[i];
         this_char->special = ConstValSpecialStatic;
         this_char->type = g->builtin_types.entry_u8;
-        bignum_init_unsigned(&this_char->data.x_bignum, (uint8_t)buf_ptr(str)[i]);
+        bigint_init_unsigned(&this_char->data.x_bigint, (uint8_t)buf_ptr(str)[i]);
     }
 }
 
@@ -3494,12 +3502,12 @@ void init_const_c_str_lit(CodeGen *g, ConstExprValue *const_val, Buf *str) {
         ConstExprValue *this_char = &array_val->data.x_array.s_none.elements[i];
         this_char->special = ConstValSpecialStatic;
         this_char->type = g->builtin_types.entry_u8;
-        bignum_init_unsigned(&this_char->data.x_bignum, (uint8_t)buf_ptr(str)[i]);
+        bigint_init_unsigned(&this_char->data.x_bigint, (uint8_t)buf_ptr(str)[i]);
     }
     ConstExprValue *null_char = &array_val->data.x_array.s_none.elements[len_with_null - 1];
     null_char->special = ConstValSpecialStatic;
     null_char->type = g->builtin_types.entry_u8;
-    bignum_init_unsigned(&null_char->data.x_bignum, 0);
+    bigint_init_unsigned(&null_char->data.x_bigint, 0);
 
     // then make the pointer point to it
     const_val->special = ConstValSpecialStatic;
@@ -3518,8 +3526,8 @@ ConstExprValue *create_const_c_str_lit(CodeGen *g, Buf *str) {
 void init_const_unsigned_negative(ConstExprValue *const_val, TypeTableEntry *type, uint64_t x, bool negative) {
     const_val->special = ConstValSpecialStatic;
     const_val->type = type;
-    bignum_init_unsigned(&const_val->data.x_bignum, x);
-    const_val->data.x_bignum.is_negative = negative;
+    bigint_init_unsigned(&const_val->data.x_bigint, x);
+    const_val->data.x_bigint.is_negative = negative;
 }
 
 ConstExprValue *create_const_unsigned_negative(TypeTableEntry *type, uint64_t x, bool negative) {
@@ -3539,7 +3547,7 @@ ConstExprValue *create_const_usize(CodeGen *g, uint64_t x) {
 void init_const_signed(ConstExprValue *const_val, TypeTableEntry *type, int64_t x) {
     const_val->special = ConstValSpecialStatic;
     const_val->type = type;
-    bignum_init_signed(&const_val->data.x_bignum, x);
+    bigint_init_signed(&const_val->data.x_bigint, x);
 }
 
 ConstExprValue *create_const_signed(TypeTableEntry *type, int64_t x) {
@@ -3551,7 +3559,7 @@ ConstExprValue *create_const_signed(TypeTableEntry *type, int64_t x) {
 void init_const_float(ConstExprValue *const_val, TypeTableEntry *type, double value) {
     const_val->special = ConstValSpecialStatic;
     const_val->type = type;
-    bignum_init_float(&const_val->data.x_bignum, value);
+    bigfloat_init_float(&const_val->data.x_bigfloat, value);
 }
 
 ConstExprValue *create_const_float(TypeTableEntry *type, double value) {
@@ -3788,12 +3796,13 @@ bool const_values_equal(ConstExprValue *a, ConstExprValue *b) {
             return a->data.x_fn.fn_entry == b->data.x_fn.fn_entry;
         case TypeTableEntryIdBool:
             return a->data.x_bool == b->data.x_bool;
-        case TypeTableEntryIdInt:
         case TypeTableEntryIdFloat:
         case TypeTableEntryIdNumLitFloat:
+            return bigfloat_cmp(&a->data.x_bigfloat, &b->data.x_bigfloat) == CmpEQ;
+        case TypeTableEntryIdInt:
         case TypeTableEntryIdNumLitInt:
         case TypeTableEntryIdEnumTag:
-            return bignum_cmp_eq(&a->data.x_bignum, &b->data.x_bignum);
+            return bigint_cmp(&a->data.x_bigint, &b->data.x_bigint) == CmpEQ;
         case TypeTableEntryIdPointer:
             if (a->data.x_ptr.special != b->data.x_ptr.special)
                 return false;
@@ -3876,58 +3885,47 @@ bool const_values_equal(ConstExprValue *a, ConstExprValue *b) {
     zig_unreachable();
 }
 
-uint64_t max_unsigned_val(TypeTableEntry *type_entry) {
-    assert(type_entry->id == TypeTableEntryIdInt);
-    if (type_entry->data.integral.bit_count == 64) {
-        return UINT64_MAX;
-    } else {
-        return (((uint64_t)1) << type_entry->data.integral.bit_count) - 1;
+void eval_min_max_value_int(CodeGen *g, TypeTableEntry *int_type, BigInt *bigint, bool is_max) {
+    assert(int_type->id == TypeTableEntryIdInt);
+    if (int_type->data.integral.bit_count == 0) {
+        bigint_init_unsigned(bigint, 0);
+        return;
     }
-}
+    if (is_max) {
+        // is_signed=true   (1 << (bit_count - 1)) - 1
+        // is_signed=false  (1 << (bit_count - 0)) - 1
+        BigInt one = {0};
+        bigint_init_unsigned(&one, 1);
 
-static int64_t max_signed_val(TypeTableEntry *type_entry) {
-    assert(type_entry->id == TypeTableEntryIdInt);
+        size_t shift_amt = int_type->data.integral.bit_count - (int_type->data.integral.is_signed ? 1 : 0);
+        BigInt bit_count_bi = {0};
+        bigint_init_unsigned(&bit_count_bi, shift_amt);
 
-    if (type_entry->data.integral.bit_count == 64) {
-        return INT64_MAX;
-    } else {
-        return (((uint64_t)1) << (type_entry->data.integral.bit_count - 1)) - 1;
-    }
-}
+        BigInt shifted_bi = {0};
+        bigint_shl(&shifted_bi, &one, &bit_count_bi);
 
-int64_t min_signed_val(TypeTableEntry *type_entry) {
-    assert(type_entry->id == TypeTableEntryIdInt);
-    if (type_entry->data.integral.bit_count == 64) {
-        return INT64_MIN;
-    } else {
-        return -((int64_t)(((uint64_t)1) << (type_entry->data.integral.bit_count - 1)));
-    }
-}
+        bigint_sub(bigint, &shifted_bi, &one);
+    } else if (int_type->data.integral.is_signed) {
+        // - (1 << (bit_count - 1))
+        BigInt one = {0};
+        bigint_init_unsigned(&one, 1);
 
-void eval_min_max_value_int(CodeGen *g, TypeTableEntry *int_type, BigNum *bignum, bool is_max) {
-    assert(int_type->id == TypeTableEntryIdInt);
-    if (is_max) {
-        if (int_type->data.integral.is_signed) {
-            int64_t val = max_signed_val(int_type);
-            bignum_init_signed(bignum, val);
-        } else {
-            uint64_t val = max_unsigned_val(int_type);
-            bignum_init_unsigned(bignum, val);
-        }
+        BigInt bit_count_bi = {0};
+        bigint_init_unsigned(&bit_count_bi, int_type->data.integral.bit_count - 1);
+
+        BigInt shifted_bi = {0};
+        bigint_shl(&shifted_bi, &one, &bit_count_bi);
+
+        bigint_negate(bigint, &shifted_bi);
     } else {
-        if (int_type->data.integral.is_signed) {
-            int64_t val = min_signed_val(int_type);
-            bignum_init_signed(bignum, val);
-        } else {
-            bignum_init_unsigned(bignum, 0);
-        }
+        bigint_init_unsigned(bigint, 0);
     }
 }
 
 void eval_min_max_value(CodeGen *g, TypeTableEntry *type_entry, ConstExprValue *const_val, bool is_max) {
     if (type_entry->id == TypeTableEntryIdInt) {
         const_val->special = ConstValSpecialStatic;
-        eval_min_max_value_int(g, type_entry, &const_val->data.x_bignum, is_max);
+        eval_min_max_value_int(g, type_entry, &const_val->data.x_bigint, is_max);
     } else if (type_entry->id == TypeTableEntryIdFloat) {
         zig_panic("TODO analyze_min_max_value float");
     } else if (type_entry->id == TypeTableEntryIdBool) {
@@ -3967,32 +3965,15 @@ void render_const_value(CodeGen *g, Buf *buf, ConstExprValue *const_val) {
             buf_appendf(buf, "{}");
             return;
         case TypeTableEntryIdNumLitFloat:
-            buf_appendf(buf, "%f", const_val->data.x_bignum.data.x_float);
+        case TypeTableEntryIdFloat:
+            bigfloat_write_buf(buf, &const_val->data.x_bigfloat);
             return;
         case TypeTableEntryIdNumLitInt:
-            {
-                BigNum *bignum = &const_val->data.x_bignum;
-                const char *negative_str = bignum->is_negative ? "-" : "";
-                buf_appendf(buf, "%s%" ZIG_PRI_llu, negative_str, bignum->data.x_uint);
-                return;
-            }
-        case TypeTableEntryIdMetaType:
-            buf_appendf(buf, "%s", buf_ptr(&const_val->data.x_type->name));
-            return;
         case TypeTableEntryIdInt:
-            {
-                BigNum *bignum = &const_val->data.x_bignum;
-                assert(bignum->kind == BigNumKindInt);
-                const char *negative_str = bignum->is_negative ? "-" : "";
-                buf_appendf(buf, "%s%" ZIG_PRI_llu, negative_str, bignum->data.x_uint);
-            }
+            bigint_write_buf(buf, &const_val->data.x_bigint, 10);
             return;
-        case TypeTableEntryIdFloat:
-            {
-                BigNum *bignum = &const_val->data.x_bignum;
-                assert(bignum->kind == BigNumKindFloat);
-                buf_appendf(buf, "%f", bignum->data.x_float);
-            }
+        case TypeTableEntryIdMetaType:
+            buf_appendf(buf, "%s", buf_ptr(&const_val->data.x_type->name));
             return;
         case TypeTableEntryIdUnreachable:
             buf_appendf(buf, "@unreachable()");
@@ -4060,7 +4041,7 @@ void render_const_value(CodeGen *g, Buf *buf, ConstExprValue *const_val) {
                     buf_append_char(buf, '"');
                     for (uint64_t i = 0; i < len; i += 1) {
                         ConstExprValue *child_value = &const_val->data.x_array.s_none.elements[i];
-                        uint64_t big_c = child_value->data.x_bignum.data.x_uint;
+                        uint64_t big_c = bigint_as_unsigned(&child_value->data.x_bigint);
                         assert(big_c <= UINT8_MAX);
                         uint8_t c = (uint8_t)big_c;
                         if (c == '"') {
@@ -4146,7 +4127,8 @@ void render_const_value(CodeGen *g, Buf *buf, ConstExprValue *const_val) {
         case TypeTableEntryIdEnumTag:
             {
                 TypeTableEntry *enum_type = type_entry->data.enum_tag.enum_type;
-                TypeEnumField *field = &enum_type->data.enumeration.fields[const_val->data.x_bignum.data.x_uint];
+                size_t field_index = bigint_as_unsigned(&const_val->data.x_bigint);
+                TypeEnumField *field = &enum_type->data.enumeration.fields[field_index];
                 buf_appendf(buf, "%s.%s", buf_ptr(&enum_type->name), buf_ptr(field->name));
                 return;
             }
src/analyze.hpp
@@ -84,9 +84,7 @@ void complete_enum(CodeGen *g, TypeTableEntry *enum_type);
 bool ir_get_var_is_comptime(VariableTableEntry *var);
 bool const_values_equal(ConstExprValue *a, ConstExprValue *b);
 void eval_min_max_value(CodeGen *g, TypeTableEntry *type_entry, ConstExprValue *const_val, bool is_max);
-void eval_min_max_value_int(CodeGen *g, TypeTableEntry *int_type, BigNum *bignum, bool is_max);
-int64_t min_signed_val(TypeTableEntry *type_entry);
-uint64_t max_unsigned_val(TypeTableEntry *type_entry);
+void eval_min_max_value_int(CodeGen *g, TypeTableEntry *int_type, BigInt *bigint, bool is_max);
 
 void render_const_value(CodeGen *g, Buf *buf, ConstExprValue *const_val);
 void define_local_param_variables(CodeGen *g, FnTableEntry *fn_table_entry, VariableTableEntry **arg_vars);
src/ast_render.cpp
@@ -182,8 +182,10 @@ static const char *node_type_str(NodeType node_type) {
             return "ErrorValueDecl";
         case NodeTypeTestDecl:
             return "TestDecl";
-        case NodeTypeNumberLiteral:
-            return "NumberLiteral";
+        case NodeTypeIntLiteral:
+            return "IntLiteral";
+        case NodeTypeFloatLiteral:
+            return "FloatLiteral";
         case NodeTypeStringLiteral:
             return "StringLiteral";
         case NodeTypeCharLiteral:
@@ -536,17 +538,20 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {
             render_node_ungrouped(ar, node->data.bin_op_expr.op2);
             if (!grouped) fprintf(ar->f, ")");
             break;
-        case NodeTypeNumberLiteral:
-            switch (node->data.number_literal.bignum->kind) {
-                case BigNumKindInt:
-                    {
-                        const char *negative_str = node->data.number_literal.bignum->is_negative ? "-" : "";
-                        fprintf(ar->f, "%s%" ZIG_PRI_llu, negative_str, node->data.number_literal.bignum->data.x_uint);
-                    }
-                    break;
-                case BigNumKindFloat:
-                    fprintf(ar->f, "%f", node->data.number_literal.bignum->data.x_float);
-                    break;
+        case NodeTypeFloatLiteral:
+            {
+                Buf rendered_buf = BUF_INIT;
+                buf_resize(&rendered_buf, 0);
+                bigfloat_write_buf(&rendered_buf, node->data.float_literal.bigfloat);
+                fprintf(ar->f, "%s", buf_ptr(&rendered_buf));
+            }
+            break;
+        case NodeTypeIntLiteral:
+            {
+                Buf rendered_buf = BUF_INIT;
+                buf_resize(&rendered_buf, 0);
+                bigint_write_buf(&rendered_buf, node->data.int_literal.bigint, 10);
+                fprintf(ar->f, "%s", buf_ptr(&rendered_buf));
             }
             break;
         case NodeTypeStringLiteral:
src/bigfloat.cpp
@@ -0,0 +1,152 @@
+/*
+ * Copyright (c) 2017 Andrew Kelley
+ *
+ * This file is part of zig, which is MIT licensed.
+ * See http://opensource.org/licenses/MIT
+ */
+
+#include "bigfloat.hpp"
+#include "bigint.hpp"
+#include "buffer.hpp"
+#include <math.h>
+#include <errno.h>
+
+void bigfloat_init_float(BigFloat *dest, long double x) {
+    dest->value = x;
+}
+
+void bigfloat_init_bigfloat(BigFloat *dest, const BigFloat *x) {
+    dest->value = x->value;
+}
+
+void bigfloat_init_bigint(BigFloat *dest, const BigInt *op) {
+    dest->value = 0.0;
+    if (op->digit_count == 0)
+        return;
+
+    long double base = (long double)UINT64_MAX;
+    const uint64_t *digits = bigint_ptr(op);
+
+    for (size_t i = op->digit_count - 1;;) {
+        uint64_t digit = digits[i];
+        dest->value *= base;
+        dest->value += (long double)digit;
+
+        if (i == 0) {
+            if (op->is_negative) {
+                dest->value = -dest->value;
+            }
+            return;
+        }
+        i -= 1;
+    }
+}
+
+int bigfloat_init_buf_base10(BigFloat *dest, const uint8_t *buf_ptr, size_t buf_len) {
+    char *str_begin = (char *)buf_ptr;
+    char *str_end;
+    errno = 0;
+    dest->value = strtold(str_begin, &str_end);
+    if (errno) {
+        return ErrorOverflow;
+    }
+    assert(str_end <= ((char*)buf_ptr) + buf_len);
+    return 0;
+}
+
+void bigfloat_add(BigFloat *dest, const BigFloat *op1, const BigFloat *op2) {
+    dest->value = op1->value + op2->value;
+}
+
+void bigfloat_negate(BigFloat *dest, const BigFloat *op) {
+    dest->value = -op->value;
+}
+
+void bigfloat_sub(BigFloat *dest, const BigFloat *op1, const BigFloat *op2) {
+    dest->value = op1->value - op2->value;
+}
+
+void bigfloat_mul(BigFloat *dest, const BigFloat *op1, const BigFloat *op2) {
+    dest->value = op1->value * op2->value;
+}
+
+void bigfloat_div(BigFloat *dest, const BigFloat *op1, const BigFloat *op2) {
+    dest->value = op1->value / op2->value;
+}
+
+void bigfloat_div_trunc(BigFloat *dest, const BigFloat *op1, const BigFloat *op2) {
+    dest->value = op1->value / op2->value;
+    if (dest->value >= 0.0) {
+        dest->value = floorl(dest->value);
+    } else {
+        dest->value = ceill(dest->value);
+    }
+}
+
+void bigfloat_div_floor(BigFloat *dest, const BigFloat *op1, const BigFloat *op2) {
+    dest->value = floorl(op1->value / op2->value);
+}
+
+void bigfloat_rem(BigFloat *dest, const BigFloat *op1, const BigFloat *op2) {
+    dest->value = fmodl(op1->value, op2->value);
+}
+
+void bigfloat_mod(BigFloat *dest, const BigFloat *op1, const BigFloat *op2) {
+    dest->value = fmodl(fmodl(op1->value, op2->value) + op2->value, op2->value);
+}
+
+void bigfloat_write_buf(Buf *buf, const BigFloat *op) {
+    buf_appendf(buf, "%Lf", op->value);
+}
+
+Cmp bigfloat_cmp(const BigFloat *op1, const BigFloat *op2) {
+    if (op1->value > op2->value) {
+        return CmpGT;
+    } else if (op1->value < op2->value) {
+        return CmpLT;
+    } else {
+        return CmpEQ;
+    }
+}
+
+// TODO this is wrong when compiler running on big endian systems. caught by tests
+void bigfloat_write_ieee597(const BigFloat *op, uint8_t *buf, size_t bit_count, bool is_big_endian) {
+    if (bit_count == 32) {
+        float f32 = op->value;
+        memcpy(buf, &f32, 4);
+    } else if (bit_count == 64) {
+        double f64 = op->value;
+        memcpy(buf, &f64, 8);
+    } else {
+        zig_unreachable();
+    }
+}
+
+// TODO this is wrong when compiler running on big endian systems. caught by tests
+void bigfloat_read_ieee597(BigFloat *dest, const uint8_t *buf, size_t bit_count, bool is_big_endian) {
+    if (bit_count == 32) {
+        float f32;
+        memcpy(&f32, buf, 4);
+        dest->value = f32;
+    } else if (bit_count == 64) {
+        double f64;
+        memcpy(&f64, buf, 8);
+        dest->value = f64;
+    } else {
+        zig_unreachable();
+    }
+}
+
+double bigfloat_to_double(const BigFloat *bigfloat) {
+    return bigfloat->value;
+}
+
+Cmp bigfloat_cmp_zero(const BigFloat *bigfloat) {
+    if (bigfloat->value < 0.0) {
+        return CmpLT;
+    } else if (bigfloat->value > 0.0) {
+        return CmpGT;
+    } else {
+        return CmpEQ;
+    }
+}
src/bigfloat.hpp
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2017 Andrew Kelley
+ *
+ * This file is part of zig, which is MIT licensed.
+ * See http://opensource.org/licenses/MIT
+ */
+
+#ifndef ZIG_BIGFLOAT_HPP
+#define ZIG_BIGFLOAT_HPP
+
+#include "bigint.hpp"
+#include "error.hpp"
+#include <stdint.h>
+#include <stddef.h>
+
+struct BigFloat {
+    long double value;
+};
+
+struct Buf;
+
+void bigfloat_init_float(BigFloat *dest, long double x);
+void bigfloat_init_bigfloat(BigFloat *dest, const BigFloat *x);
+void bigfloat_init_bigint(BigFloat *dest, const BigInt *op);
+int bigfloat_init_buf_base10(BigFloat *dest, const uint8_t *buf_ptr, size_t buf_len);
+
+double bigfloat_to_double(const BigFloat *bigfloat);
+
+void bigfloat_add(BigFloat *dest, const BigFloat *op1, const BigFloat *op2);
+void bigfloat_negate(BigFloat *dest, const BigFloat *op);
+void bigfloat_sub(BigFloat *dest, const BigFloat *op1, const BigFloat *op2);
+void bigfloat_mul(BigFloat *dest, const BigFloat *op1, const BigFloat *op2);
+void bigfloat_div(BigFloat *dest, const BigFloat *op1, const BigFloat *op2);
+void bigfloat_div_trunc(BigFloat *dest, const BigFloat *op1, const BigFloat *op2);
+void bigfloat_div_floor(BigFloat *dest, const BigFloat *op1, const BigFloat *op2);
+void bigfloat_rem(BigFloat *dest, const BigFloat *op1, const BigFloat *op2);
+void bigfloat_mod(BigFloat *dest, const BigFloat *op1, const BigFloat *op2);
+void bigfloat_write_buf(Buf *buf, const BigFloat *op);
+Cmp bigfloat_cmp(const BigFloat *op1, const BigFloat *op2);
+void bigfloat_write_ieee597(const BigFloat *op, uint8_t *buf, size_t bit_count, bool is_big_endian);
+void bigfloat_read_ieee597(BigFloat *dest, const uint8_t *buf, size_t bit_count, bool is_big_endian);
+
+
+// convenience functions
+Cmp bigfloat_cmp_zero(const BigFloat *bigfloat);
+
+#endif
src/bigint.cpp
@@ -0,0 +1,1088 @@
+/*
+ * Copyright (c) 2017 Andrew Kelley
+ *
+ * This file is part of zig, which is MIT licensed.
+ * See http://opensource.org/licenses/MIT
+ */
+
+#include "bigfloat.hpp"
+#include "bigint.hpp"
+#include "buffer.hpp"
+#include "list.hpp"
+#include "os.hpp"
+
+static void bigint_normalize(BigInt *dest) {
+    const uint64_t *digits = bigint_ptr(dest);
+
+    size_t last_nonzero_digit = SIZE_MAX;
+    for (size_t i = 0; i < dest->digit_count; i += 1) {
+        uint64_t digit = digits[i];
+        if (digit != 0) {
+            last_nonzero_digit = i;
+        }
+    }
+    if (last_nonzero_digit == SIZE_MAX) {
+        dest->is_negative = false;
+        dest->digit_count = 0;
+    } else {
+        dest->digit_count = last_nonzero_digit + 1;
+        if (last_nonzero_digit == 0) {
+            dest->data.digit = digits[0];
+        }
+    }
+}
+
+static uint8_t digit_to_char(uint8_t digit, bool uppercase) {
+    if (digit <= 9) {
+        return digit + '0';
+    } else if (digit <= 35) {
+        return digit + (uppercase ? 'A' : 'a');
+    } else {
+        zig_unreachable();
+    }
+}
+
+size_t bigint_bits_needed(const BigInt *op) {
+    size_t full_bits = op->digit_count * 64;
+    size_t leading_zero_count = bigint_clz(op, full_bits);
+    size_t bits_needed = full_bits - leading_zero_count;
+    return bits_needed + op->is_negative;
+}
+
+static void to_twos_complement(BigInt *dest, const BigInt *op, size_t bit_count) {
+    if (bit_count == 0 || op->digit_count == 0) {
+        bigint_init_unsigned(dest, 0);
+        return;
+    }
+    if (op->is_negative) {
+        BigInt negated = {0};
+        bigint_negate(&negated, op);
+
+        BigInt inverted = {0};
+        bigint_not(&inverted, &negated, bit_count, false);
+
+        BigInt one = {0};
+        bigint_init_unsigned(&one, 1);
+
+        bigint_add(dest, &inverted, &one);
+        return;
+    }
+
+    dest->is_negative = false;
+    const uint64_t *op_digits = bigint_ptr(op);
+    if (op->digit_count == 1) {
+        dest->data.digit = op_digits[0];
+        if (bit_count < 64) {
+            dest->data.digit &= (1ULL << bit_count) - 1;
+        }
+        dest->digit_count = 1;
+        bigint_normalize(dest);
+        return;
+    }
+    size_t digits_to_copy = bit_count / 64;
+    size_t leftover_bits = bit_count % 64;
+    dest->digit_count = digits_to_copy + ((leftover_bits == 0) ? 0 : 1);
+    dest->data.digits = allocate_nonzero<uint64_t>(dest->digit_count);
+    for (size_t i = 0; i < digits_to_copy; i += 1) {
+        uint64_t digit = (i < op->digit_count) ? op_digits[i] : 0;
+        dest->data.digits[i] = digit;
+    }
+    if (leftover_bits != 0) {
+        uint64_t digit = (digits_to_copy < op->digit_count) ? op_digits[digits_to_copy] : 0;
+        dest->data.digits[digits_to_copy] = digit & ((1ULL << leftover_bits) - 1);
+    }
+    bigint_normalize(dest);
+}
+
+static bool bit_at_index(const BigInt *bi, size_t index) {
+    size_t digit_index = bi->digit_count - (index / 64) - 1;
+    size_t digit_bit_index = index % 64;
+    const uint64_t *digits = bigint_ptr(bi);
+    uint64_t digit = digits[digit_index];
+    return ((digit >> digit_bit_index) & 0x1) == 0x1;
+}
+
+static void from_twos_complement(BigInt *dest, const BigInt *src, size_t bit_count, bool is_signed) {
+    assert(!src->is_negative);
+
+    if (bit_count == 0 || src->digit_count == 0) {
+        bigint_init_unsigned(dest, 0);
+        return;
+    }
+
+    if (is_signed && bit_at_index(src, bit_count - 1)) {
+        BigInt negative_one = {0};
+        bigint_init_signed(&negative_one, -1);
+
+        BigInt minus_one = {0};
+        bigint_add(&minus_one, src, &negative_one);
+
+        BigInt inverted = {0};
+        bigint_not(&inverted, &minus_one, bit_count, false);
+
+        bigint_negate(dest, &inverted);
+        return;
+
+    }
+
+    bigint_init_bigint(dest, src);
+}
+
+void bigint_init_unsigned(BigInt *dest, uint64_t x) {
+    if (x == 0) {
+        dest->digit_count = 0;
+        dest->is_negative = false;
+        return;
+    }
+    dest->digit_count = 1;
+    dest->data.digit = x;
+    dest->is_negative = false;
+}
+
+void bigint_init_signed(BigInt *dest, int64_t x) {
+    if (x >= 0) {
+        return bigint_init_unsigned(dest, x);
+    }
+    dest->is_negative = true;
+    dest->digit_count = 1;
+    dest->data.digit = ((uint64_t)(-(x + 1))) + 1;
+}
+
+void bigint_init_bigint(BigInt *dest, const BigInt *src) {
+    if (src->digit_count == 0) {
+        return bigint_init_unsigned(dest, 0);
+    } else if (src->digit_count == 1) {
+        dest->digit_count = 1;
+        dest->data.digit = src->data.digit;
+        dest->is_negative = src->is_negative;
+        return;
+    }
+    dest->is_negative = src->is_negative;
+    dest->digit_count = src->digit_count;
+    dest->data.digits = allocate_nonzero<uint64_t>(dest->digit_count);
+    memcpy(dest->data.digits, src->data.digits, sizeof(uint64_t) * dest->digit_count);
+}
+
+void bigint_init_bigfloat(BigInt *dest, const BigFloat *op) {
+    if (op->value >= 0) {
+        bigint_init_unsigned(dest, op->value);
+    } else {
+        bigint_init_unsigned(dest, -op->value);
+        dest->is_negative = true;
+    }
+}
+
+bool bigint_fits_in_bits(const BigInt *bn, size_t bit_count, bool is_signed) {
+    assert(bn->digit_count != 1 || bn->data.digit != 0);
+    if (bit_count == 0) {
+        return bigint_cmp_zero(bn) == CmpEQ;
+    }
+    if (bn->digit_count == 0) {
+        return true;
+    }
+
+    if (!is_signed) {
+        size_t full_bits = bn->digit_count * 64;
+        size_t leading_zero_count = bigint_clz(bn, full_bits);
+        return bit_count >= full_bits - leading_zero_count;
+    }
+
+    BigInt one = {0};
+    bigint_init_unsigned(&one, 1);
+
+    BigInt shl_amt = {0};
+    bigint_init_unsigned(&shl_amt, bit_count - 1);
+
+    BigInt max_value_plus_one = {0};
+    bigint_shl(&max_value_plus_one, &one, &shl_amt);
+
+    BigInt max_value = {0};
+    bigint_sub(&max_value, &max_value_plus_one, &one);
+
+    BigInt min_value = {0};
+    bigint_negate(&min_value, &max_value_plus_one);
+
+    Cmp min_cmp = bigint_cmp(bn, &min_value);
+    Cmp max_cmp = bigint_cmp(bn, &max_value);
+
+    return (min_cmp == CmpGT || min_cmp == CmpEQ) && (max_cmp == CmpLT || max_cmp == CmpEQ);
+}
+
+void bigint_write_twos_complement(const BigInt *big_int, uint8_t *buf, size_t bit_count, bool is_big_endian) {
+    if (bit_count == 0)
+        return;
+
+    BigInt twos_comp = {0};
+    to_twos_complement(&twos_comp, big_int, bit_count);
+
+    const uint64_t *twos_comp_digits = bigint_ptr(&twos_comp);
+
+    size_t bits_in_last_digit = bit_count % 64;
+    size_t bytes_in_last_digit = (bits_in_last_digit + 7) / 8;
+    size_t unwritten_byte_count = 8 - bytes_in_last_digit;
+
+    if (is_big_endian) {
+        size_t last_digit_index = (bit_count - 1) / 64;
+        size_t digit_index = last_digit_index;
+        size_t buf_index = 0;
+        for (;;) {
+            uint64_t x = (digit_index < twos_comp.digit_count) ? twos_comp_digits[digit_index] : 0;
+
+            for (size_t byte_index = 7;;) {
+                uint8_t byte = x & 0xff;
+                if (digit_index == last_digit_index) {
+                    buf[buf_index + byte_index - unwritten_byte_count] = byte;
+                    if (byte_index == unwritten_byte_count) break;
+                } else {
+                    buf[buf_index + byte_index] = byte;
+                }
+
+                if (byte_index == 0) break;
+                byte_index -= 1;
+                x >>= 8;
+            }
+
+            if (digit_index == 0) break;
+            digit_index -= 1;
+            if (digit_index == last_digit_index) {
+                buf_index += bytes_in_last_digit;
+            } else {
+                buf_index += 8;
+            }
+        }
+    } else {
+        size_t digit_count = (bit_count + 63) / 64;
+        size_t buf_index = 0;
+        for (size_t digit_index = 0; digit_index < digit_count; digit_index += 1) {
+            uint64_t x = (digit_index < twos_comp.digit_count) ? twos_comp_digits[digit_index] : 0;
+
+            for (size_t byte_index = 0; byte_index < 8; byte_index += 1) {
+                uint8_t byte = x & 0xff;
+                buf[buf_index] = byte;
+                buf_index += 1;
+                if (buf_index >= unwritten_byte_count) {
+                    break;
+                }
+                x >>= 8;
+            }
+        }
+    }
+}
+
+
+void bigint_read_twos_complement(BigInt *dest, const uint8_t *buf, size_t bit_count, bool is_big_endian,
+        bool is_signed)
+{
+    if (bit_count == 0) {
+        bigint_init_unsigned(dest, 0);
+        return;
+    }
+
+    dest->digit_count = (bit_count + 63) / 64;
+    uint64_t *digits;
+    if (dest->digit_count == 1) {
+        digits = &dest->data.digit;
+    } else {
+        digits = allocate_nonzero<uint64_t>(dest->digit_count);
+        dest->data.digits = digits;
+    }
+
+    size_t bits_in_last_digit = bit_count % 64;
+    if (bits_in_last_digit == 0) {
+        bits_in_last_digit = 64;
+    }
+    size_t bytes_in_last_digit = (bits_in_last_digit + 7) / 8;
+    size_t unread_byte_count = 8 - bytes_in_last_digit;
+
+    if (is_big_endian) {
+        size_t buf_index = 0;
+        uint64_t digit = 0;
+        for (size_t byte_index = unread_byte_count; byte_index < 8; byte_index += 1) {
+            uint8_t byte = buf[buf_index];
+            buf_index += 1;
+            digit <<= 8;
+            digit |= byte;
+        }
+        digits[dest->digit_count - 1] = digit;
+        for (size_t digit_index = 1; digit_index < dest->digit_count; digit_index += 1) {
+            digit = 0;
+            for (size_t byte_index = 0; byte_index < 8; byte_index += 1) {
+                uint8_t byte = buf[buf_index];
+                buf_index += 1;
+                digit <<= 8;
+                digit |= byte;
+            }
+            digits[dest->digit_count - 1 - digit_index] = digit;
+        }
+    } else {
+        size_t buf_index = 0;
+        for (size_t digit_index = 0; digit_index < dest->digit_count; digit_index += 1) {
+            uint64_t digit = 0;
+            size_t end_byte_index = (digit_index == dest->digit_count - 1) ? bytes_in_last_digit : 8;
+            for (size_t byte_index = 0; byte_index < end_byte_index; byte_index += 1) {
+                uint64_t byte = buf[buf_index];
+                buf_index += 1;
+
+                digit |= byte << (8 * byte_index);
+            }
+            digits[digit_index] = digit;
+        }
+    }
+
+    if (is_signed) {
+        bigint_normalize(dest);
+        BigInt tmp = {0};
+        bigint_init_bigint(&tmp, dest);
+        from_twos_complement(dest, &tmp, bit_count, true);
+    } else {
+        dest->is_negative = false;
+        bigint_normalize(dest);
+    }
+}
+
+static bool add_u64_overflow(uint64_t op1, uint64_t op2, uint64_t *result) {
+    return __builtin_uaddll_overflow((unsigned long long)op1, (unsigned long long)op2,
+            (unsigned long long *)result);
+}
+
+static bool sub_u64_overflow(uint64_t op1, uint64_t op2, uint64_t *result) {
+    return __builtin_usubll_overflow((unsigned long long)op1, (unsigned long long)op2,
+            (unsigned long long *)result);
+}
+
+static bool mul_u64_overflow(uint64_t op1, uint64_t op2, uint64_t *result) {
+    return __builtin_umulll_overflow((unsigned long long)op1, (unsigned long long)op2,
+            (unsigned long long *)result);
+}
+
+void bigint_add(BigInt *dest, const BigInt *op1, const BigInt *op2) {
+    if (op1->digit_count == 0) {
+        return bigint_init_bigint(dest, op2);
+    }
+    if (op2->digit_count == 0) {
+        return bigint_init_bigint(dest, op1);
+    }
+    if (op1->is_negative == op2->is_negative) {
+        dest->is_negative = op1->is_negative;
+
+        const uint64_t *op1_digits = bigint_ptr(op1);
+        const uint64_t *op2_digits = bigint_ptr(op2);
+        uint64_t overflow = add_u64_overflow(op1_digits[0], op2_digits[0], &dest->data.digit);
+        if (overflow == 0 && op1->digit_count == 1 && op2->digit_count == 1) {
+            dest->digit_count = 1;
+            bigint_normalize(dest);
+            return;
+        }
+        // TODO this code path is untested
+        size_t i = 1;
+        uint64_t first_digit = dest->data.digit;
+        dest->data.digits = allocate_nonzero<uint64_t>(max(op1->digit_count, op2->digit_count) + 1);
+        dest->data.digits[0] = first_digit;
+
+        for (;;) {
+            bool found_digit = false;
+            uint64_t x = overflow;
+            overflow = 0;
+
+            if (i < op1->digit_count) {
+                found_digit = true;
+                uint64_t digit = op1_digits[i];
+                overflow += add_u64_overflow(x, digit, &x);
+            }
+
+            if (i < op2->digit_count) {
+                found_digit = true;
+                uint64_t digit = op2_digits[i];
+                overflow += add_u64_overflow(x, digit, &x);
+            }
+
+            dest->data.digits[i] = x;
+            x += 1;
+
+            if (!found_digit) {
+                break;
+            }
+        }
+        if (overflow > 0) {
+            dest->data.digits[i] = overflow;
+        }
+        bigint_normalize(dest);
+        return;
+    }
+    const BigInt *op_pos;
+    const BigInt *op_neg;
+    if (op1->is_negative) {
+        op_neg = op1;
+        op_pos = op2;
+    } else {
+        op_pos = op1;
+        op_neg = op2;
+    }
+
+    BigInt op_neg_abs = {0};
+    bigint_negate(&op_neg_abs, op_neg);
+    const BigInt *bigger_op;
+    const BigInt *smaller_op;
+    switch (bigint_cmp(op_pos, &op_neg_abs)) {
+        case CmpEQ:
+            bigint_init_unsigned(dest, 0);
+            return;
+        case CmpLT:
+            bigger_op = &op_neg_abs;
+            smaller_op = op_pos;
+            dest->is_negative = true;
+            break;
+        case CmpGT:
+            bigger_op = op_pos;
+            smaller_op = &op_neg_abs;
+            dest->is_negative = false;
+            break;
+    }
+    const uint64_t *bigger_op_digits = bigint_ptr(bigger_op);
+    const uint64_t *smaller_op_digits = bigint_ptr(smaller_op);
+    uint64_t overflow = sub_u64_overflow(bigger_op_digits[0], smaller_op_digits[0], &dest->data.digit);
+    if (overflow == 0 && bigger_op->digit_count == 1 && smaller_op->digit_count == 1) {
+        dest->digit_count = 1;
+        bigint_normalize(dest);
+        return;
+    }
+    uint64_t first_digit = dest->data.digit;
+    dest->data.digits = allocate_nonzero<uint64_t>(bigger_op->digit_count);
+    dest->data.digits[0] = first_digit;
+    size_t i = 1;
+
+    for (;;) {
+        bool found_digit = false;
+        uint64_t x = bigger_op_digits[i];
+        uint64_t prev_overflow = overflow;
+        overflow = 0;
+
+        if (i < smaller_op->digit_count) {
+            found_digit = true;
+            uint64_t digit = smaller_op_digits[i];
+            overflow += sub_u64_overflow(x, digit, &x);
+        }
+        if (sub_u64_overflow(x, prev_overflow, &x)) {
+            found_digit = true;
+            overflow += 1;
+        }
+        dest->data.digits[i] = x;
+        i += 1;
+
+        if (!found_digit)
+            break;
+    }
+    assert(overflow == 0);
+    dest->digit_count = i;
+    bigint_normalize(dest);
+}
+
+void bigint_add_wrap(BigInt *dest, const BigInt *op1, const BigInt *op2, size_t bit_count, bool is_signed) {
+    BigInt unwrapped = {0};
+    bigint_add(&unwrapped, op1, op2);
+    bigint_truncate(dest, &unwrapped, bit_count, is_signed);
+}
+
+void bigint_sub(BigInt *dest, const BigInt *op1, const BigInt *op2) {
+    BigInt op2_negated = {0};
+    bigint_negate(&op2_negated, op2);
+    return bigint_add(dest, op1, &op2_negated);
+}
+
+void bigint_sub_wrap(BigInt *dest, const BigInt *op1, const BigInt *op2, size_t bit_count, bool is_signed) {
+    BigInt op2_negated = {0};
+    bigint_negate(&op2_negated, op2);
+    return bigint_add_wrap(dest, op1, &op2_negated, bit_count, is_signed);
+}
+
+static void mul_overflow(uint64_t x, uint64_t y, uint64_t *result, uint64_t *carry) {
+    if (!mul_u64_overflow(x, y, result)) {
+        *carry = 0;
+        return;
+    }
+    zig_panic("TODO bigint_mul with big numbers");
+
+    //unsigned __int128 big_x = x;
+    //unsigned __int128 big_y = y;
+    //unsigned __int128 big_result = big_x * big_y;
+    //*carry = big_result >> 64;
+}
+
+void bigint_mul(BigInt *dest, const BigInt *op1, const BigInt *op2) {
+    if (op1->digit_count == 0 || op2->digit_count == 0) {
+        return bigint_init_unsigned(dest, 0);
+    }
+    const uint64_t *op1_digits = bigint_ptr(op1);
+    const uint64_t *op2_digits = bigint_ptr(op2);
+
+    uint64_t carry;
+    mul_overflow(op1_digits[0], op2_digits[0], &dest->data.digit, &carry);
+    if (carry == 0 && op1->digit_count == 1 && op2->digit_count == 1) {
+        dest->is_negative = (op1->is_negative != op2->is_negative);
+        dest->digit_count = 1;
+        bigint_normalize(dest);
+        return;
+    }
+    zig_panic("TODO bigint_mul with big numbers");
+}
+
+void bigint_mul_wrap(BigInt *dest, const BigInt *op1, const BigInt *op2, size_t bit_count, bool is_signed) {
+    BigInt unwrapped = {0};
+    bigint_mul(&unwrapped, op1, op2);
+    bigint_truncate(dest, &unwrapped, bit_count, is_signed);
+}
+
+void bigint_div_trunc(BigInt *dest, const BigInt *op1, const BigInt *op2) {
+    assert(op2->digit_count != 0); // division by zero
+    if (op1->digit_count == 0) {
+        bigint_init_unsigned(dest, 0);
+        return;
+    }
+    if (op1->digit_count != 1 || op2->digit_count != 1) {
+        zig_panic("TODO bigint div_trunc with >1 digits");
+    }
+    const uint64_t *op1_digits = bigint_ptr(op1);
+    const uint64_t *op2_digits = bigint_ptr(op2);
+    dest->data.digit = op1_digits[0] / op2_digits[0];
+    dest->digit_count = 1;
+    dest->is_negative = op1->is_negative != op2->is_negative;
+    bigint_normalize(dest);
+}
+
+void bigint_div_floor(BigInt *dest, const BigInt *op1, const BigInt *op2) {
+    if (op1->is_negative != op2->is_negative) {
+        bigint_div_trunc(dest, op1, op2);
+        BigInt mult_again = {0};
+        bigint_mul(&mult_again, dest, op2);
+        mult_again.is_negative = op1->is_negative;
+        if (bigint_cmp(&mult_again, op1) != CmpEQ) {
+            BigInt tmp = {0};
+            bigint_init_bigint(&tmp, dest);
+            BigInt neg_one = {0};
+            bigint_init_signed(&neg_one, -1);
+            bigint_add(dest, &tmp, &neg_one);
+        }
+        bigint_normalize(dest);
+    } else {
+        bigint_div_trunc(dest, op1, op2);
+    }
+}
+
+void bigint_rem(BigInt *dest, const BigInt *op1, const BigInt *op2) {
+    assert(op2->digit_count != 0); // division by zero
+    if (op1->digit_count == 0) {
+        bigint_init_unsigned(dest, 0);
+        return;
+    }
+    const uint64_t *op1_digits = bigint_ptr(op1);
+    const uint64_t *op2_digits = bigint_ptr(op2);
+    if (op2->digit_count == 2 && op2_digits[0] == 0 && op2_digits[1] == 1) {
+        // special case this divisor
+        bigint_init_unsigned(dest, op1_digits[0]);
+        dest->is_negative = op1->is_negative;
+        bigint_normalize(dest);
+        return;
+    }
+    if (op1->digit_count != 1 || op2->digit_count != 1) {
+        zig_panic("TODO bigint rem with >1 digits");
+    }
+    dest->data.digit = op1_digits[0] % op2_digits[0];
+    dest->digit_count = 1;
+    dest->is_negative = op1->is_negative;
+    bigint_normalize(dest);
+}
+
+void bigint_mod(BigInt *dest, const BigInt *op1, const BigInt *op2) {
+    if (op1->is_negative) {
+        BigInt first_rem;
+        bigint_rem(&first_rem, op1, op2);
+        first_rem.is_negative = !op2->is_negative;
+        BigInt op2_minus_rem;
+        bigint_add(&op2_minus_rem, op2, &first_rem);
+        bigint_rem(dest, &op2_minus_rem, op2);
+        dest->is_negative = false;
+    } else {
+        bigint_rem(dest, op1, op2);
+        dest->is_negative = false;
+    }
+}
+
+void bigint_or(BigInt *dest, const BigInt *op1, const BigInt *op2) {
+    if (op1->digit_count == 0) {
+        return bigint_init_bigint(dest, op2);
+    }
+    if (op2->digit_count == 0) {
+        return bigint_init_bigint(dest, op1);
+    }
+    if (op1->is_negative || op2->is_negative) {
+        // TODO this code path is untested
+        size_t big_bit_count = max(bigint_bits_needed(op1), bigint_bits_needed(op2));
+
+        BigInt twos_comp_op1 = {0};
+        to_twos_complement(&twos_comp_op1, op1, big_bit_count);
+
+        BigInt twos_comp_op2 = {0};
+        to_twos_complement(&twos_comp_op2, op2, big_bit_count);
+
+        BigInt twos_comp_dest = {0};
+        bigint_or(&twos_comp_dest, &twos_comp_op1, &twos_comp_op2);
+
+        from_twos_complement(dest, &twos_comp_dest, big_bit_count, true);
+    } else {
+        dest->is_negative = false;
+        const uint64_t *op1_digits = bigint_ptr(op1);
+        const uint64_t *op2_digits = bigint_ptr(op2);
+        if (op1->digit_count == 1 && op2->digit_count == 1) {
+            dest->digit_count = 1;
+            dest->data.digit = op1_digits[0] | op2_digits[0];
+            bigint_normalize(dest);
+            return;
+        }
+        // TODO this code path is untested
+        uint64_t first_digit = dest->data.digit;
+        dest->digit_count = max(op1->digit_count, op2->digit_count);
+        dest->data.digits = allocate_nonzero<uint64_t>(dest->digit_count);
+        dest->data.digits[0] = first_digit;
+        size_t i = 1;
+        for (; i < dest->digit_count; i += 1) {
+            uint64_t digit = 0;
+            if (i < op1->digit_count) {
+                digit |= op1_digits[i];
+            }
+            if (i < op2->digit_count) {
+                digit |= op2_digits[i];
+            }
+            dest->data.digits[i] = digit;
+        }
+        bigint_normalize(dest);
+    }
+}
+
+void bigint_and(BigInt *dest, const BigInt *op1, const BigInt *op2) {
+    if (op1->digit_count == 0 || op2->digit_count == 0) {
+        return bigint_init_unsigned(dest, 0);
+    }
+    if (op1->is_negative || op2->is_negative) {
+        // TODO this code path is untested
+        size_t big_bit_count = max(bigint_bits_needed(op1), bigint_bits_needed(op2));
+
+        BigInt twos_comp_op1 = {0};
+        to_twos_complement(&twos_comp_op1, op1, big_bit_count);
+
+        BigInt twos_comp_op2 = {0};
+        to_twos_complement(&twos_comp_op2, op2, big_bit_count);
+
+        BigInt twos_comp_dest = {0};
+        bigint_and(&twos_comp_dest, &twos_comp_op1, &twos_comp_op2);
+
+        from_twos_complement(dest, &twos_comp_dest, big_bit_count, true);
+    } else {
+        dest->is_negative = false;
+        const uint64_t *op1_digits = bigint_ptr(op1);
+        const uint64_t *op2_digits = bigint_ptr(op2);
+        if (op1->digit_count == 1 && op2->digit_count == 1) {
+            dest->digit_count = 1;
+            dest->data.digit = op1_digits[0] & op2_digits[0];
+            bigint_normalize(dest);
+            return;
+        }
+        // TODO this code path is untested
+        uint64_t first_digit = dest->data.digit;
+        dest->digit_count = max(op1->digit_count, op2->digit_count);
+        dest->data.digits = allocate_nonzero<uint64_t>(dest->digit_count);
+        dest->data.digits[0] = first_digit;
+        size_t i = 1;
+        for (; i < op1->digit_count && i < op2->digit_count; i += 1) {
+            dest->data.digits[i] = op1_digits[i] & op2_digits[i];
+        }
+        for (; i < dest->digit_count; i += 1) {
+            dest->data.digits[i] = 0;
+        }
+        bigint_normalize(dest);
+    }
+}
+
+void bigint_xor(BigInt *dest, const BigInt *op1, const BigInt *op2) {
+    if (op1->is_negative || op2->is_negative) {
+        // TODO this code path is untested
+        size_t big_bit_count = max(bigint_bits_needed(op1), bigint_bits_needed(op2));
+
+        BigInt twos_comp_op1 = {0};
+        to_twos_complement(&twos_comp_op1, op1, big_bit_count);
+
+        BigInt twos_comp_op2 = {0};
+        to_twos_complement(&twos_comp_op2, op2, big_bit_count);
+
+        BigInt twos_comp_dest = {0};
+        bigint_xor(&twos_comp_dest, &twos_comp_op1, &twos_comp_op2);
+
+        from_twos_complement(dest, &twos_comp_dest, big_bit_count, true);
+    } else {
+        dest->is_negative = false;
+        const uint64_t *op1_digits = bigint_ptr(op1);
+        const uint64_t *op2_digits = bigint_ptr(op2);
+        if (op1->digit_count == 1 && op2->digit_count == 1) {
+            dest->digit_count = 1;
+            dest->data.digit = op1_digits[0] ^ op2_digits[0];
+            bigint_normalize(dest);
+            return;
+        }
+        // TODO this code path is untested
+        uint64_t first_digit = dest->data.digit;
+        dest->digit_count = max(op1->digit_count, op2->digit_count);
+        dest->data.digits = allocate_nonzero<uint64_t>(dest->digit_count);
+        dest->data.digits[0] = first_digit;
+        size_t i = 1;
+        for (; i < op1->digit_count && i < op2->digit_count; i += 1) {
+            dest->data.digits[i] = op1_digits[i] ^ op2_digits[i];
+        }
+        for (; i < dest->digit_count; i += 1) {
+            if (i < op1->digit_count) {
+                dest->data.digits[i] = op1_digits[i];
+            }
+            if (i < op2->digit_count) {
+                dest->data.digits[i] = op2_digits[i];
+            }
+        }
+        bigint_normalize(dest);
+    }
+}
+
+void bigint_shl(BigInt *dest, const BigInt *op1, const BigInt *op2) {
+    assert(!op2->is_negative);
+
+    if (op2->digit_count == 0) {
+        bigint_init_bigint(dest, op1);
+        return;
+    }
+
+    if (op1->digit_count == 0) {
+        bigint_init_unsigned(dest, 0);
+        return;
+    }
+
+    if (op2->digit_count != 1) {
+        zig_panic("TODO shift left by amount greater than 64 bit integer");
+    }
+
+    const uint64_t *op1_digits = bigint_ptr(op1);
+    uint64_t shift_amt = bigint_as_unsigned(op2);
+
+    if (op1->digit_count == 1) {
+        dest->data.digit = op1_digits[0] << shift_amt;
+        if (dest->data.digit > op1_digits[0]) {
+            dest->digit_count = 1;
+            dest->is_negative = op1->is_negative;
+            return;
+        }
+    }
+
+    uint64_t digit_shift_count = shift_amt / 64;
+    uint64_t leftover_shift_count = shift_amt % 64;
+
+    dest->data.digits = allocate<uint64_t>(op1->digit_count + digit_shift_count + 1);
+    dest->digit_count = digit_shift_count;
+    uint64_t carry = 0;
+    for (size_t i = 0; i < op1->digit_count; i += 1) {
+        uint64_t digit = op1_digits[i];
+        dest->data.digits[dest->digit_count] = carry | (digit << leftover_shift_count);
+        dest->digit_count += 1;
+        if (leftover_shift_count > 0) {
+            carry = digit >> (64 - leftover_shift_count);
+        } else {
+            carry = 0;
+        }
+    }
+    dest->data.digits[dest->digit_count] = carry;
+    dest->digit_count += 1;
+    dest->is_negative = op1->is_negative;
+    bigint_normalize(dest);
+}
+
+void bigint_shl_wrap(BigInt *dest, const BigInt *op1, const BigInt *op2, size_t bit_count, bool is_signed) {
+    BigInt unwrapped = {0};
+    bigint_shl(&unwrapped, op1, op2);
+    bigint_truncate(dest, &unwrapped, bit_count, is_signed);
+}
+
+void bigint_shr(BigInt *dest, const BigInt *op1, const BigInt *op2) {
+    assert(!op2->is_negative);
+
+    if (op1->digit_count == 0) {
+        return bigint_init_unsigned(dest, 0);
+    }
+
+    if (op2->digit_count == 0) {
+        return bigint_init_bigint(dest, op1);
+    }
+
+    if (op2->digit_count != 1) {
+        zig_panic("TODO shift right by amount greater than 64 bit integer");
+    }
+
+    const uint64_t *op1_digits = bigint_ptr(op1);
+    uint64_t shift_amt = bigint_as_unsigned(op2);
+
+    if (op1->digit_count == 1) {
+        dest->data.digit = op1_digits[0] >> shift_amt;
+        dest->digit_count = 1;
+        dest->is_negative = op1->is_negative;
+        bigint_normalize(dest);
+        return;
+    }
+
+    // TODO this code path is untested
+    size_t digit_shift_count = shift_amt / 64;
+    size_t leftover_shift_count = shift_amt % 64;
+
+    if (digit_shift_count >= op1->digit_count) {
+        return bigint_init_unsigned(dest, 0);
+    }
+
+    dest->digit_count = op1->digit_count - digit_shift_count;
+    dest->data.digits = allocate<uint64_t>(dest->digit_count);
+    uint64_t carry = 0;
+    for (size_t op_digit_index = op1->digit_count - 1;;) {
+        uint64_t digit = op1_digits[op_digit_index];
+        size_t dest_digit_index = op_digit_index - digit_shift_count;
+        dest->data.digits[dest_digit_index] = carry | (digit >> leftover_shift_count);
+        carry = (0xffffffffffffffffULL << leftover_shift_count) & digit;
+
+        if (dest_digit_index == 0) { break; }
+        op_digit_index -= 1;
+    }
+    dest->is_negative = op1->is_negative;
+    bigint_normalize(dest);
+}
+
+void bigint_negate(BigInt *dest, const BigInt *op) {
+    bigint_init_bigint(dest, op);
+    dest->is_negative = !dest->is_negative;
+    bigint_normalize(dest);
+}
+
+void bigint_negate_wrap(BigInt *dest, const BigInt *op, size_t bit_count) {
+    BigInt zero;
+    bigint_init_unsigned(&zero, 0);
+    bigint_sub_wrap(dest, &zero, op, bit_count, true);
+}
+
+void bigint_not(BigInt *dest, const BigInt *op, size_t bit_count, bool is_signed) {
+    if (bit_count == 0) {
+        bigint_init_unsigned(dest, 0);
+        return;
+    }
+
+    if (is_signed) {
+        BigInt twos_comp = {0};
+        to_twos_complement(&twos_comp, op, bit_count);
+
+        BigInt inverted = {0};
+        bigint_not(&inverted, &twos_comp, bit_count, false);
+
+        from_twos_complement(dest, &inverted, bit_count, true);
+        return;
+    }
+
+    assert(!op->is_negative);
+
+    dest->is_negative = false;
+    const uint64_t *op_digits = bigint_ptr(op);
+    if (bit_count <= 64) {
+        dest->digit_count = 1;
+        if (op->digit_count == 0) {
+            if (bit_count == 64) {
+                dest->data.digit = UINT64_MAX;
+            } else {
+                dest->data.digit = (1ULL << bit_count) - 1;
+            }
+        } else if (op->digit_count == 1) {
+            dest->data.digit = ~op_digits[0];
+            if (bit_count != 64) {
+                uint64_t mask = (1ULL << bit_count) - 1;
+                dest->data.digit &= mask;
+            }
+        }
+        bigint_normalize(dest);
+        return;
+    }
+    // TODO this code path is untested
+    dest->digit_count = bit_count / 64;
+    assert(dest->digit_count >= op->digit_count);
+    dest->data.digits = allocate_nonzero<uint64_t>(dest->digit_count);
+    size_t i = 0;
+    for (; i < op->digit_count; i += 1) {
+        dest->data.digits[i] = ~op_digits[i];
+    }
+    for (; i < dest->digit_count; i += 1) {
+        dest->data.digits[i] = 0xffffffffffffffffULL;
+    }
+    size_t digit_index = dest->digit_count - (bit_count / 64) - 1;
+    size_t digit_bit_index = bit_count % 64;
+    if (digit_index < dest->digit_count) {
+        uint64_t mask = (1ULL << digit_bit_index) - 1;
+        dest->data.digits[digit_index] &= mask;
+    }
+    bigint_normalize(dest);
+}
+
+void bigint_truncate(BigInt *dest, const BigInt *op, size_t bit_count, bool is_signed) {
+    BigInt twos_comp;
+    to_twos_complement(&twos_comp, op, bit_count);
+    from_twos_complement(dest, &twos_comp, bit_count, is_signed);
+}
+
+Cmp bigint_cmp(const BigInt *op1, const BigInt *op2) {
+    if (op1->is_negative && !op2->is_negative) {
+        return CmpLT;
+    } else if (!op1->is_negative && op2->is_negative) {
+        return CmpGT;
+    } else if (op1->digit_count > op2->digit_count) {
+        return op1->is_negative ? CmpLT : CmpGT;
+    } else if (op2->digit_count > op1->digit_count) {
+        return op1->is_negative ? CmpGT : CmpLT;
+    } else if (op1->digit_count == 0) {
+        return CmpEQ;
+    }
+    const uint64_t *op1_digits = bigint_ptr(op1);
+    const uint64_t *op2_digits = bigint_ptr(op2);
+    for (size_t i = op1->digit_count - 1; ;) {
+        uint64_t op1_digit = op1_digits[i];
+        uint64_t op2_digit = op2_digits[i];
+
+        if (op1_digit > op2_digit) {
+            return op1->is_negative ? CmpLT : CmpGT;
+        }
+        if (op1_digit < op2_digit) {
+            return op1->is_negative ? CmpGT : CmpLT;
+        }
+
+        if (i == 0) {
+            return CmpEQ;
+        }
+        i -= 1;
+    }
+}
+
+void bigint_write_buf(Buf *buf, const BigInt *op, uint64_t base) {
+    if (op->digit_count == 0) {
+        buf_append_char(buf, '0');
+        return;
+    }
+    if (op->is_negative) {
+        buf_append_char(buf, '-');
+    }
+    if (op->digit_count == 1 && base == 10) {
+        buf_appendf(buf, "%" ZIG_PRI_u64, op->data.digit);
+        return;
+    }
+    // TODO this code path is untested
+    size_t first_digit_index = buf_len(buf);
+
+    BigInt digit_bi = {0};
+    BigInt a1 = {0};
+    BigInt a2 = {0};
+
+    BigInt *a = &a1;
+    BigInt *other_a = &a2;
+    bigint_init_bigint(a, op);
+
+    BigInt base_bi = {0};
+    bigint_init_unsigned(&base_bi, 10);
+
+    for (;;) {
+        bigint_rem(&digit_bi, a, &base_bi);
+        uint8_t digit = bigint_as_unsigned(&digit_bi);
+        buf_append_char(buf, digit_to_char(digit, false));
+        bigint_div_trunc(other_a, a, &base_bi);
+        {
+            BigInt *tmp = a;
+            a = other_a;
+            other_a = tmp;
+        }
+        if (bigint_cmp_zero(a) == CmpEQ) {
+            break;
+        }
+    }
+
+    // reverse
+    for (size_t i = first_digit_index; i < buf_len(buf); i += 1) {
+        size_t other_i = buf_len(buf) + first_digit_index - i - 1;
+        uint8_t tmp = buf_ptr(buf)[i];
+        buf_ptr(buf)[i] = buf_ptr(buf)[other_i];
+        buf_ptr(buf)[other_i] = tmp;
+    }
+}
+
+size_t bigint_ctz(const BigInt *bi, size_t bit_count) {
+    if (bit_count == 0)
+        return 0;
+    if (bi->digit_count == 0)
+        return bit_count;
+
+    BigInt twos_comp = {0};
+    to_twos_complement(&twos_comp, bi, bit_count);
+
+    size_t count = 0;
+    for (size_t i = 0; i < bit_count; i += 1) {
+        if (bit_at_index(&twos_comp, i))
+            return count;
+        count += 1;
+    }
+    return count;
+}
+
+size_t bigint_clz(const BigInt *bi, size_t bit_count) {
+    if (bi->is_negative || bit_count == 0)
+        return 0;
+    if (bi->digit_count == 0)
+        return bit_count;
+
+    size_t count = 0;
+    for (size_t i = bit_count - 1;;) {
+        if (bit_at_index(bi, i))
+            return count;
+        count += 1;
+
+        if (i == 0) break;
+        i -= 1;
+    }
+    return count;
+}
+
+uint64_t bigint_as_unsigned(const BigInt *bigint) {
+    assert(!bigint->is_negative);
+    if (bigint->digit_count == 0) {
+        return 0;
+    } else if (bigint->digit_count == 1) {
+        return bigint->data.digit;
+    } else {
+        zig_unreachable();
+    }
+}
+
+int64_t bigint_as_signed(const BigInt *bigint) {
+    if (bigint->digit_count == 0) {
+        return 0;
+    } else if (bigint->digit_count == 1) {
+        if (bigint->is_negative) {
+            // TODO this code path is untested
+            if (bigint->data.digit <= 9223372036854775808ULL) {
+                return (-((int64_t)(bigint->data.digit - 1))) - 1;
+            } else {
+                zig_unreachable();
+            }
+        } else {
+            return bigint->data.digit;
+        }
+    } else {
+        zig_unreachable();
+    }
+}
+
+Cmp bigint_cmp_zero(const BigInt *op) {
+    if (op->digit_count == 0) {
+        return CmpEQ;
+    }
+    return op->is_negative ? CmpLT : CmpGT;
+}
src/bigint.hpp
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2017 Andrew Kelley
+ *
+ * This file is part of zig, which is MIT licensed.
+ * See http://opensource.org/licenses/MIT
+ */
+
+#ifndef ZIG_BIGINT_HPP
+#define ZIG_BIGINT_HPP
+
+#include <stdint.h>
+#include <stddef.h>
+
+struct BigInt {
+    size_t digit_count;
+    union {
+        uint64_t digit;
+        uint64_t *digits; // Least significant digit first
+    } data;
+    bool is_negative;
+};
+
+struct Buf;
+struct BigFloat;
+
+enum Cmp {
+    CmpLT,
+    CmpGT,
+    CmpEQ,
+};
+
+void bigint_init_unsigned(BigInt *dest, uint64_t x);
+void bigint_init_signed(BigInt *dest, int64_t x);
+void bigint_init_bigint(BigInt *dest, const BigInt *src);
+void bigint_init_bigfloat(BigInt *dest, const BigFloat *op);
+
+// panics if number won't fit
+uint64_t bigint_as_unsigned(const BigInt *bigint);
+int64_t bigint_as_signed(const BigInt *bigint);
+
+static inline const uint64_t *bigint_ptr(const BigInt *bigint) {
+    if (bigint->digit_count == 1) {
+        return &bigint->data.digit;
+    } else {
+        return bigint->data.digits;
+    }
+}
+
+bool bigint_fits_in_bits(const BigInt *bn, size_t bit_count, bool is_signed);
+void bigint_write_twos_complement(const BigInt *big_int, uint8_t *buf, size_t bit_count, bool is_big_endian);
+void bigint_read_twos_complement(BigInt *dest, const uint8_t *buf, size_t bit_count, bool is_big_endian,
+        bool is_signed);
+void bigint_add(BigInt *dest, const BigInt *op1, const BigInt *op2);
+void bigint_add_wrap(BigInt *dest, const BigInt *op1, const BigInt *op2, size_t bit_count, bool is_signed);
+void bigint_sub(BigInt *dest, const BigInt *op1, const BigInt *op2);
+void bigint_sub_wrap(BigInt *dest, const BigInt *op1, const BigInt *op2, size_t bit_count, bool is_signed);
+void bigint_mul(BigInt *dest, const BigInt *op1, const BigInt *op2);
+void bigint_mul_wrap(BigInt *dest, const BigInt *op1, const BigInt *op2, size_t bit_count, bool is_signed);
+void bigint_div_trunc(BigInt *dest, const BigInt *op1, const BigInt *op2);
+void bigint_div_floor(BigInt *dest, const BigInt *op1, const BigInt *op2);
+void bigint_rem(BigInt *dest, const BigInt *op1, const BigInt *op2);
+void bigint_mod(BigInt *dest, const BigInt *op1, const BigInt *op2);
+
+void bigint_or(BigInt *dest, const BigInt *op1, const BigInt *op2);
+void bigint_and(BigInt *dest, const BigInt *op1, const BigInt *op2);
+void bigint_xor(BigInt *dest, const BigInt *op1, const BigInt *op2);
+
+void bigint_shl(BigInt *dest, const BigInt *op1, const BigInt *op2);
+void bigint_shl_wrap(BigInt *dest, const BigInt *op1, const BigInt *op2, size_t bit_count, bool is_signed);
+void bigint_shr(BigInt *dest, const BigInt *op1, const BigInt *op2);
+
+void bigint_negate(BigInt *dest, const BigInt *op);
+void bigint_negate_wrap(BigInt *dest, const BigInt *op, size_t bit_count);
+void bigint_not(BigInt *dest, const BigInt *op, size_t bit_count, bool is_signed);
+void bigint_truncate(BigInt *dest, const BigInt *op, size_t bit_count, bool is_signed);
+
+Cmp bigint_cmp(const BigInt *op1, const BigInt *op2);
+
+void bigint_write_buf(Buf *buf, const BigInt *op, uint64_t base);
+
+size_t bigint_ctz(const BigInt *bi, size_t bit_count);
+size_t bigint_clz(const BigInt *bi, size_t bit_count);
+
+size_t bigint_bits_needed(const BigInt *op);
+
+
+// convenience functions
+Cmp bigint_cmp_zero(const BigInt *op);
+
+#endif
src/bignum.cpp
@@ -1,535 +0,0 @@
-/*
- * Copyright (c) 2016 Andrew Kelley
- *
- * This file is part of zig, which is MIT licensed.
- * See http://opensource.org/licenses/MIT
- */
-
-#include "bignum.hpp"
-#include "buffer.hpp"
-#include "os.hpp"
-
-#include <assert.h>
-#include <math.h>
-#include <inttypes.h>
-
-static void bignum_normalize(BigNum *bn) {
-    assert(bn->kind == BigNumKindInt);
-    if (bn->data.x_uint == 0) {
-        bn->is_negative = false;
-    }
-}
-
-void bignum_init_float(BigNum *dest, double x) {
-    dest->kind = BigNumKindFloat;
-    dest->is_negative = false;
-    dest->data.x_float = x;
-}
-
-void bignum_init_unsigned(BigNum *dest, uint64_t x) {
-    dest->kind = BigNumKindInt;
-    dest->is_negative = false;
-    dest->data.x_uint = x;
-}
-
-void bignum_init_signed(BigNum *dest, int64_t x) {
-    dest->kind = BigNumKindInt;
-    if (x < 0) {
-        dest->is_negative = true;
-        dest->data.x_uint = ((uint64_t)(-(x + 1))) + 1;
-    } else {
-        dest->is_negative = false;
-        dest->data.x_uint = x;
-    }
-}
-
-void bignum_init_bignum(BigNum *dest, BigNum *src) {
-    safe_memcpy(dest, src, 1);
-}
-
-static int u64_log2(uint64_t x) {
-    int result = 0;
-    for (; x != 0; x >>= 1) {
-        result += 1;
-    }
-    return result;
-}
-
-bool bignum_fits_in_bits(BigNum *bn, int bit_count, bool is_signed) {
-    assert(bn->kind == BigNumKindInt);
-
-    if (is_signed) {
-        uint64_t max_neg;
-        uint64_t max_pos;
-        if (bit_count < 64) {
-            max_neg = (1ULL << (bit_count - 1));
-            max_pos = max_neg - 1;
-        } else {
-            max_pos = ((uint64_t)INT64_MAX);
-            max_neg = max_pos + 1;
-        }
-        uint64_t max_val = bn->is_negative ? max_neg : max_pos;
-        return bn->data.x_uint <= max_val;
-    } else {
-        if (bn->is_negative) {
-            return bn->data.x_uint == 0;
-        } else {
-            int required_bit_count = u64_log2(bn->data.x_uint);
-            return bit_count >= required_bit_count;
-        }
-    }
-}
-
-void bignum_truncate(BigNum *bn, int bit_count) {
-    assert(bn->kind == BigNumKindInt);
-    // TODO handle case when negative = true
-    if (bit_count < 64) {
-        bn->data.x_uint &= (1LL << bit_count) - 1;
-    }
-}
-
-uint64_t bignum_to_twos_complement(BigNum *bn) {
-    assert(bn->kind == BigNumKindInt);
-
-    if (bn->is_negative) {
-        int64_t x = bn->data.x_uint;
-        return -x;
-    } else {
-        return bn->data.x_uint;
-    }
-}
-
-// returns true if overflow happened
-bool bignum_add(BigNum *dest, BigNum *op1, BigNum *op2) {
-    assert(op1->kind == op2->kind);
-    dest->kind = op1->kind;
-
-    if (dest->kind == BigNumKindFloat) {
-        dest->data.x_float = op1->data.x_float + op2->data.x_float;
-        return false;
-    }
-
-    if (op1->is_negative == op2->is_negative) {
-        dest->is_negative = op1->is_negative;
-        return __builtin_uaddll_overflow(op1->data.x_uint, op2->data.x_uint, &dest->data.x_uint);
-    } else if (!op1->is_negative && op2->is_negative) {
-        if (__builtin_usubll_overflow(op1->data.x_uint, op2->data.x_uint, &dest->data.x_uint)) {
-            dest->data.x_uint = (UINT64_MAX - dest->data.x_uint) + 1;
-            dest->is_negative = true;
-            bignum_normalize(dest);
-            return false;
-        } else {
-            bignum_normalize(dest);
-            return false;
-        }
-    } else {
-        return bignum_add(dest, op2, op1);
-    }
-}
-
-void bignum_negate(BigNum *dest, BigNum *op) {
-    dest->kind = op->kind;
-
-    if (dest->kind == BigNumKindFloat) {
-        dest->data.x_float = -op->data.x_float;
-    } else {
-        dest->data.x_uint = op->data.x_uint;
-        dest->is_negative = !op->is_negative;
-        bignum_normalize(dest);
-    }
-}
-
-void bignum_not(BigNum *dest, BigNum *op, int bit_count, bool is_signed) {
-    assert(op->kind == BigNumKindInt);
-    uint64_t bits = ~bignum_to_twos_complement(op);
-    if (bit_count < 64) {
-        bits &= (1LL << bit_count) - 1;
-    }
-    if (is_signed)
-        bignum_init_signed(dest, bits);
-    else
-        bignum_init_unsigned(dest, bits);
-}
-
-void bignum_cast_to_float(BigNum *dest, BigNum *op) {
-    assert(op->kind == BigNumKindInt);
-    dest->kind = BigNumKindFloat;
-
-    dest->data.x_float = (double)op->data.x_uint;
-
-    if (op->is_negative) {
-        dest->data.x_float = -dest->data.x_float;
-    }
-}
-
-void bignum_cast_to_int(BigNum *dest, BigNum *op) {
-    assert(op->kind == BigNumKindFloat);
-    dest->kind = BigNumKindInt;
-
-    if (op->data.x_float >= 0) {
-        dest->data.x_uint = (unsigned long long)op->data.x_float;
-        dest->is_negative = false;
-    } else {
-        dest->data.x_uint = (unsigned long long)-op->data.x_float;
-        dest->is_negative = true;
-    }
-}
-
-bool bignum_sub(BigNum *dest, BigNum *op1, BigNum *op2) {
-    BigNum op2_negated;
-    bignum_negate(&op2_negated, op2);
-    return bignum_add(dest, op1, &op2_negated);
-}
-
-bool bignum_mul(BigNum *dest, BigNum *op1, BigNum *op2) {
-    assert(op1->kind == op2->kind);
-    dest->kind = op1->kind;
-
-    if (dest->kind == BigNumKindFloat) {
-        dest->data.x_float = op1->data.x_float * op2->data.x_float;
-        return false;
-    }
-
-    if (__builtin_umulll_overflow(op1->data.x_uint, op2->data.x_uint, &dest->data.x_uint)) {
-        return true;
-    }
-
-    dest->is_negative = op1->is_negative != op2->is_negative;
-    bignum_normalize(dest);
-    return false;
-}
-
-bool bignum_div(BigNum *dest, BigNum *op1, BigNum *op2) {
-    assert(op1->kind == op2->kind);
-    dest->kind = op1->kind;
-
-    if (dest->kind == BigNumKindFloat) {
-        dest->data.x_float = op1->data.x_float / op2->data.x_float;
-    } else {
-        return bignum_div_trunc(dest, op1, op2);
-    }
-    return false;
-}
-
-bool bignum_div_trunc(BigNum *dest, BigNum *op1, BigNum *op2) {
-    assert(op1->kind == op2->kind);
-    dest->kind = op1->kind;
-
-    if (dest->kind == BigNumKindFloat) {
-        double result = op1->data.x_float / op2->data.x_float;
-        if (result >= 0) {
-            dest->data.x_float = floor(result);
-        } else {
-            dest->data.x_float = ceil(result);
-        }
-    } else {
-        dest->data.x_uint = op1->data.x_uint / op2->data.x_uint;
-        dest->is_negative = op1->is_negative != op2->is_negative;
-        bignum_normalize(dest);
-    }
-    return false;
-}
-
-bool bignum_div_floor(BigNum *dest, BigNum *op1, BigNum *op2) {
-    assert(op1->kind == op2->kind);
-    dest->kind = op1->kind;
-
-    if (dest->kind == BigNumKindFloat) {
-        dest->data.x_float = floor(op1->data.x_float / op2->data.x_float);
-    } else {
-        if (op1->is_negative != op2->is_negative) {
-            uint64_t result = op1->data.x_uint / op2->data.x_uint;
-            if (result * op2->data.x_uint == op1->data.x_uint) {
-                dest->data.x_uint = result;
-            } else {
-                dest->data.x_uint = result + 1;
-            }
-            dest->is_negative = true;
-        } else {
-            dest->data.x_uint = op1->data.x_uint / op2->data.x_uint;
-            dest->is_negative = false;
-        }
-    }
-    return false;
-}
-
-bool bignum_rem(BigNum *dest, BigNum *op1, BigNum *op2) {
-    assert(op1->kind == op2->kind);
-    dest->kind = op1->kind;
-
-    if (dest->kind == BigNumKindFloat) {
-        dest->data.x_float = fmod(op1->data.x_float, op2->data.x_float);
-    } else {
-        dest->data.x_uint = op1->data.x_uint % op2->data.x_uint;
-        dest->is_negative = op1->is_negative;
-        bignum_normalize(dest);
-    }
-    return false;
-}
-
-bool bignum_mod(BigNum *dest, BigNum *op1, BigNum *op2) {
-    assert(op1->kind == op2->kind);
-    dest->kind = op1->kind;
-
-    if (dest->kind == BigNumKindFloat) {
-        dest->data.x_float = fmod(fmod(op1->data.x_float, op2->data.x_float) + op2->data.x_float, op2->data.x_float);
-    } else {
-        if (op1->is_negative) {
-            dest->data.x_uint = (op2->data.x_uint - op1->data.x_uint % op2->data.x_uint) % op2->data.x_uint;
-        } else {
-            dest->data.x_uint = op1->data.x_uint % op2->data.x_uint;
-        }
-        dest->is_negative = false;
-        bignum_normalize(dest);
-    }
-    return false;
-}
-
-bool bignum_or(BigNum *dest, BigNum *op1, BigNum *op2) {
-    assert(op1->kind == BigNumKindInt);
-    assert(op2->kind == BigNumKindInt);
-
-    assert(!op1->is_negative);
-    assert(!op2->is_negative);
-
-    dest->kind = BigNumKindInt;
-    dest->data.x_uint = op1->data.x_uint | op2->data.x_uint;
-    return false;
-}
-
-bool bignum_and(BigNum *dest, BigNum *op1, BigNum *op2) {
-    assert(op1->kind == BigNumKindInt);
-    assert(op2->kind == BigNumKindInt);
-
-    assert(!op1->is_negative);
-    assert(!op2->is_negative);
-
-    dest->kind = BigNumKindInt;
-    dest->data.x_uint = op1->data.x_uint & op2->data.x_uint;
-    return false;
-}
-
-bool bignum_xor(BigNum *dest, BigNum *op1, BigNum *op2) {
-    assert(op1->kind == BigNumKindInt);
-    assert(op2->kind == BigNumKindInt);
-
-    assert(!op1->is_negative);
-    assert(!op2->is_negative);
-
-    dest->kind = BigNumKindInt;
-    dest->data.x_uint = op1->data.x_uint ^ op2->data.x_uint;
-    return false;
-}
-
-bool bignum_shl(BigNum *dest, BigNum *op1, BigNum *op2) {
-    assert(op1->kind == BigNumKindInt);
-    assert(op2->kind == BigNumKindInt);
-
-    assert(!op1->is_negative);
-    assert(!op2->is_negative);
-
-    dest->kind = BigNumKindInt;
-    dest->data.x_uint = op1->data.x_uint << op2->data.x_uint;
-    return false;
-}
-
-bool bignum_shr(BigNum *dest, BigNum *op1, BigNum *op2) {
-    assert(op1->kind == BigNumKindInt);
-    assert(op2->kind == BigNumKindInt);
-
-    assert(!op1->is_negative);
-    assert(!op2->is_negative);
-
-    dest->kind = BigNumKindInt;
-    dest->data.x_uint = op1->data.x_uint >> op2->data.x_uint;
-    return false;
-}
-
-
-Buf *bignum_to_buf(BigNum *bn) {
-    if (bn->kind == BigNumKindFloat) {
-        return buf_sprintf("%f", bn->data.x_float);
-    } else {
-        const char *neg = bn->is_negative ? "-" : "";
-        return buf_sprintf("%s%" ZIG_PRI_llu "", neg, bn->data.x_uint);
-    }
-}
-
-bool bignum_cmp_eq(BigNum *op1, BigNum *op2) {
-    assert(op1->kind == op2->kind);
-    if (op1->kind == BigNumKindFloat) {
-        return op1->data.x_float == op2->data.x_float;
-    } else {
-        return op1->data.x_uint == op2->data.x_uint &&
-            (op1->is_negative == op2->is_negative || op1->data.x_uint == 0);
-    }
-}
-
-bool bignum_cmp_neq(BigNum *op1, BigNum *op2) {
-    return !bignum_cmp_eq(op1, op2);
-}
-
-bool bignum_cmp_lt(BigNum *op1, BigNum *op2) {
-    return !bignum_cmp_gte(op1, op2);
-}
-
-bool bignum_cmp_gt(BigNum *op1, BigNum *op2) {
-    return !bignum_cmp_lte(op1, op2);
-}
-
-bool bignum_cmp_lte(BigNum *op1, BigNum *op2) {
-    assert(op1->kind == op2->kind);
-    if (op1->kind == BigNumKindFloat) {
-        return (op1->data.x_float <= op2->data.x_float);
-    }
-
-    // assume normalized is_negative
-    if (!op1->is_negative && !op2->is_negative) {
-        return op1->data.x_uint <= op2->data.x_uint;
-    } else if (op1->is_negative && op2->is_negative) {
-        return op1->data.x_uint >= op2->data.x_uint;
-    } else if (op1->is_negative && !op2->is_negative) {
-        return true;
-    } else {
-        return false;
-    }
-}
-
-bool bignum_cmp_gte(BigNum *op1, BigNum *op2) {
-    assert(op1->kind == op2->kind);
-
-    if (op1->kind == BigNumKindFloat) {
-        return (op1->data.x_float >= op2->data.x_float);
-    }
-
-    // assume normalized is_negative
-    if (!op1->is_negative && !op2->is_negative) {
-        return op1->data.x_uint >= op2->data.x_uint;
-    } else if (op1->is_negative && op2->is_negative) {
-        return op1->data.x_uint <= op2->data.x_uint;
-    } else if (op1->is_negative && !op2->is_negative) {
-        return false;
-    } else {
-        return true;
-    }
-}
-
-bool bignum_increment_by_scalar(BigNum *bignum, uint64_t scalar) {
-    assert(bignum->kind == BigNumKindInt);
-    assert(!bignum->is_negative);
-    return __builtin_uaddll_overflow(bignum->data.x_uint, scalar, &bignum->data.x_uint);
-}
-
-bool bignum_multiply_by_scalar(BigNum *bignum, uint64_t scalar) {
-    assert(bignum->kind == BigNumKindInt);
-    assert(!bignum->is_negative);
-    return __builtin_umulll_overflow(bignum->data.x_uint, scalar, &bignum->data.x_uint);
-}
-
-uint32_t bignum_ctz(BigNum *bignum, uint32_t bit_count) {
-    assert(bignum->kind == BigNumKindInt);
-
-    uint64_t x = bignum_to_twos_complement(bignum);
-    uint32_t result = 0;
-    for (uint32_t i = 0; i < bit_count; i += 1) {
-        if ((x & 0x1) != 0)
-            break;
-
-        result += 1;
-        x = x >> 1;
-    }
-    return result;
-}
-
-uint32_t bignum_clz(BigNum *bignum, uint32_t bit_count) {
-    assert(bignum->kind == BigNumKindInt);
-
-    if (bit_count == 0)
-        return 0;
-
-    uint64_t x = bignum_to_twos_complement(bignum);
-    uint64_t mask = ((uint64_t)1) << ((uint64_t)bit_count - 1);
-    uint32_t result = 0;
-    for (uint32_t i = 0; i < bit_count; i += 1) {
-        if ((x & mask) != 0)
-            break;
-
-        result += 1;
-        x = x << 1;
-    }
-    return result;
-}
-
-void bignum_write_twos_complement(BigNum *bn, uint8_t *buf, int bit_count, bool is_big_endian) {
-    assert(bn->kind == BigNumKindInt);
-    uint64_t x = bignum_to_twos_complement(bn);
-
-    int byte_count = (bit_count + 7) / 8;
-    for (int i = 0; i < byte_count; i += 1) {
-        uint8_t le_byte = (x >> (i * 8)) & 0xff;
-        if (is_big_endian) {
-            buf[byte_count - i - 1] = le_byte;
-        } else {
-            buf[i] = le_byte;
-        }
-    }
-}
-
-void bignum_read_twos_complement(BigNum *bn, uint8_t *buf, int bit_count, bool is_big_endian, bool is_signed) {
-    int byte_count = (bit_count + 7) / 8;
-
-    uint64_t twos_comp = 0;
-    for (int i = 0; i < byte_count; i += 1) {
-        uint8_t be_byte;
-        if (is_big_endian) {
-            be_byte = buf[i];
-        } else {
-            be_byte = buf[byte_count - i - 1];
-        }
-
-        twos_comp <<= 8;
-        twos_comp |= be_byte;
-    }
-
-    uint8_t be_byte = buf[is_big_endian ? 0 : byte_count - 1];
-    if (is_signed && ((be_byte >> 7) & 0x1) != 0) {
-        bn->is_negative = true;
-        uint64_t mask = 0;
-        for (int i = 0; i < bit_count; i += 1) {
-            mask <<= 1;
-            mask |= 1;
-        }
-        bn->data.x_uint = ((~twos_comp) & mask) + 1;
-    } else {
-        bn->data.x_uint = twos_comp;
-    }
-    bn->kind = BigNumKindInt;
-}
-
-void bignum_write_ieee597(BigNum *bn, uint8_t *buf, int bit_count, bool is_big_endian) {
-    assert(bn->kind == BigNumKindFloat);
-    if (bit_count == 32) {
-        float f32 = bn->data.x_float;
-        memcpy(buf, &f32, 4);
-    } else if (bit_count == 64) {
-        double f64 = bn->data.x_float;
-        memcpy(buf, &f64, 8);
-    } else {
-        zig_unreachable();
-    }
-}
-
-void bignum_read_ieee597(BigNum *bn, uint8_t *buf, int bit_count, bool is_big_endian) {
-    bn->kind = BigNumKindFloat;
-    if (bit_count == 32) {
-        float f32;
-        memcpy(&f32, buf, 4);
-        bn->data.x_float = f32;
-    } else if (bit_count == 64) {
-        double f64;
-        memcpy(&f64, buf, 8);
-        bn->data.x_float = f64;
-    } else {
-        zig_unreachable();
-    }
-}
src/bignum.hpp
@@ -1,81 +0,0 @@
-/*
- * Copyright (c) 2016 Andrew Kelley
- *
- * This file is part of zig, which is MIT licensed.
- * See http://opensource.org/licenses/MIT
- */
-
-#ifndef ZIG_BIGNUM_HPP
-#define ZIG_BIGNUM_HPP
-
-#include <stdint.h>
-
-enum BigNumKind {
-    BigNumKindInt,
-    BigNumKindFloat,
-};
-
-struct BigNum {
-    BigNumKind kind;
-    bool is_negative;
-    union {
-        unsigned long long x_uint;
-        double x_float;
-    } data;
-};
-
-void bignum_init_float(BigNum *dest, double x);
-void bignum_init_unsigned(BigNum *dest, uint64_t x);
-void bignum_init_signed(BigNum *dest, int64_t x);
-void bignum_init_bignum(BigNum *dest, BigNum *src);
-
-bool bignum_fits_in_bits(BigNum *bn, int bit_count, bool is_signed);
-uint64_t bignum_to_twos_complement(BigNum *bn);
-
-void bignum_write_twos_complement(BigNum *bn, uint8_t *buf, int bit_count, bool is_big_endian);
-void bignum_write_ieee597(BigNum *bn, uint8_t *buf, int bit_count, bool is_big_endian);
-void bignum_read_twos_complement(BigNum *bn, uint8_t *buf, int bit_count, bool is_big_endian, bool is_signed);
-void bignum_read_ieee597(BigNum *bn, uint8_t *buf, int bit_count, bool is_big_endian);
-
-// returns true if overflow happened
-bool bignum_add(BigNum *dest, BigNum *op1, BigNum *op2);
-bool bignum_sub(BigNum *dest, BigNum *op1, BigNum *op2);
-bool bignum_mul(BigNum *dest, BigNum *op1, BigNum *op2);
-bool bignum_div(BigNum *dest, BigNum *op1, BigNum *op2);
-bool bignum_div_trunc(BigNum *dest, BigNum *op1, BigNum *op2);
-bool bignum_div_floor(BigNum *dest, BigNum *op1, BigNum *op2);
-bool bignum_rem(BigNum *dest, BigNum *op1, BigNum *op2);
-bool bignum_mod(BigNum *dest, BigNum *op1, BigNum *op2);
-
-bool bignum_or(BigNum *dest, BigNum *op1, BigNum *op2);
-bool bignum_and(BigNum *dest, BigNum *op1, BigNum *op2);
-bool bignum_xor(BigNum *dest, BigNum *op1, BigNum *op2);
-bool bignum_shl(BigNum *dest, BigNum *op1, BigNum *op2);
-bool bignum_shr(BigNum *dest, BigNum *op1, BigNum *op2);
-
-void bignum_negate(BigNum *dest, BigNum *op);
-void bignum_cast_to_float(BigNum *dest, BigNum *op);
-void bignum_cast_to_int(BigNum *dest, BigNum *op);
-void bignum_not(BigNum *dest, BigNum *op, int bit_count, bool is_signed);
-
-void bignum_truncate(BigNum *dest, int bit_count);
-
-// returns the result of the comparison
-bool bignum_cmp_eq(BigNum *op1, BigNum *op2);
-bool bignum_cmp_neq(BigNum *op1, BigNum *op2);
-bool bignum_cmp_lt(BigNum *op1, BigNum *op2);
-bool bignum_cmp_gt(BigNum *op1, BigNum *op2);
-bool bignum_cmp_lte(BigNum *op1, BigNum *op2);
-bool bignum_cmp_gte(BigNum *op1, BigNum *op2);
-
-// helper functions
-bool bignum_increment_by_scalar(BigNum *bignum, uint64_t scalar);
-bool bignum_multiply_by_scalar(BigNum *bignum, uint64_t scalar);
-
-struct Buf;
-Buf *bignum_to_buf(BigNum *bn);
-
-uint32_t bignum_ctz(BigNum *bignum, uint32_t bit_count);
-uint32_t bignum_clz(BigNum *bignum, uint32_t bit_count);
-
-#endif
src/codegen.cpp
@@ -1203,6 +1203,23 @@ enum DivKind {
     DivKindExact,
 };
 
+static LLVMValueRef bigint_to_llvm_const(LLVMTypeRef type_ref, BigInt *bigint) {
+    if (bigint->digit_count == 0) {
+        return LLVMConstNull(type_ref);
+    }
+    LLVMValueRef unsigned_val = LLVMConstIntOfArbitraryPrecision(type_ref,
+            bigint->digit_count, bigint_ptr(bigint));
+    if (bigint->is_negative) {
+        return LLVMConstNeg(unsigned_val);
+    } else {
+        return unsigned_val;
+    }
+}
+
+static LLVMValueRef bigfloat_to_llvm_const(LLVMTypeRef type_ref, BigFloat *bigfloat) {
+    return LLVMConstReal(type_ref, bigfloat_to_double(bigfloat));
+}
+
 static LLVMValueRef gen_div(CodeGen *g, bool want_debug_safety, bool want_fast_math,
         LLVMValueRef val1, LLVMValueRef val2,
         TypeTableEntry *type_entry, DivKind div_kind)
@@ -1230,7 +1247,9 @@ static LLVMValueRef gen_div(CodeGen *g, bool want_debug_safety, bool want_fast_m
 
         if (type_entry->id == TypeTableEntryIdInt && type_entry->data.integral.is_signed) {
             LLVMValueRef neg_1_value = LLVMConstInt(type_entry->type_ref, -1, true);
-            LLVMValueRef int_min_value = LLVMConstInt(type_entry->type_ref, min_signed_val(type_entry), true);
+            BigInt int_min_bi = {0};
+            eval_min_max_value_int(g, type_entry, &int_min_bi, false);
+            LLVMValueRef int_min_value = bigint_to_llvm_const(type_entry->type_ref, &int_min_bi);
             LLVMBasicBlockRef overflow_ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivOverflowOk");
             LLVMBasicBlockRef overflow_fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivOverflowFail");
             LLVMValueRef num_is_int_min = LLVMBuildICmp(g->builder, LLVMIntEQ, val1, int_min_value, "");
@@ -1765,8 +1784,13 @@ static LLVMValueRef ir_render_int_to_err(CodeGen *g, IrExecutable *executable, I
         LLVMValueRef zero = LLVMConstNull(actual_type->type_ref);
         LLVMValueRef neq_zero_bit = LLVMBuildICmp(g->builder, LLVMIntNE, target_val, zero, "");
         LLVMValueRef ok_bit;
-        uint64_t biggest_possible_err_val = max_unsigned_val(actual_type);
-        if (biggest_possible_err_val < g->error_decls.length) {
+
+        BigInt biggest_possible_err_val = {0};
+        eval_min_max_value_int(g, actual_type, &biggest_possible_err_val, true);
+
+        if (bigint_fits_in_bits(&biggest_possible_err_val, 64, false) &&
+            bigint_as_unsigned(&biggest_possible_err_val) < g->error_decls.length)
+        {
             ok_bit = neq_zero_bit;
         } else {
             LLVMValueRef error_value_count = LLVMConstInt(actual_type->type_ref, g->error_decls.length, false);
@@ -3317,7 +3341,6 @@ static LLVMValueRef pack_const_int(CodeGen *g, LLVMTypeRef big_int_type_ref, Con
                 LLVMValueRef int_val = gen_const_val(g, const_val);
                 return LLVMConstZExt(int_val, big_int_type_ref);
             }
-            return LLVMConstInt(big_int_type_ref, bignum_to_twos_complement(&const_val->data.x_bignum), false);
         case TypeTableEntryIdFloat:
             {
                 LLVMValueRef float_val = gen_const_val(g, const_val);
@@ -3374,21 +3397,13 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val) {
     switch (type_entry->id) {
         case TypeTableEntryIdInt:
         case TypeTableEntryIdEnumTag:
-            return LLVMConstInt(type_entry->type_ref, bignum_to_twos_complement(&const_val->data.x_bignum), false);
+            return bigint_to_llvm_const(type_entry->type_ref, &const_val->data.x_bigint);
         case TypeTableEntryIdPureError:
             assert(const_val->data.x_pure_err);
             return LLVMConstInt(g->builtin_types.entry_pure_error->type_ref,
                     const_val->data.x_pure_err->value, false);
         case TypeTableEntryIdFloat:
-            if (const_val->data.x_bignum.kind == BigNumKindFloat) {
-                return LLVMConstReal(type_entry->type_ref, const_val->data.x_bignum.data.x_float);
-            } else {
-                double x = (double)const_val->data.x_bignum.data.x_uint;
-                if (const_val->data.x_bignum.is_negative) {
-                    x = -x;
-                }
-                return LLVMConstReal(type_entry->type_ref, x);
-            }
+            return bigfloat_to_llvm_const(type_entry->type_ref, &const_val->data.x_bigfloat);
         case TypeTableEntryIdBool:
             if (const_val->data.x_bool) {
                 return LLVMConstAllOnes(LLVMInt1Type());
@@ -3866,7 +3881,7 @@ static void do_code_gen(CodeGen *g) {
             ConstExprValue *const_val = var->value;
             assert(const_val->special != ConstValSpecialRuntime);
             TypeTableEntry *var_type = g->builtin_types.entry_f64;
-            LLVMValueRef init_val = LLVMConstReal(var_type->type_ref, const_val->data.x_bignum.data.x_float);
+            LLVMValueRef init_val = bigfloat_to_llvm_const(var_type->type_ref, &const_val->data.x_bigfloat);
             gen_global_var(g, var, init_val, var_type);
             continue;
         }
@@ -3875,10 +3890,9 @@ static void do_code_gen(CodeGen *g) {
             // Generate debug info for it but that's it.
             ConstExprValue *const_val = var->value;
             assert(const_val->special != ConstValSpecialRuntime);
-            TypeTableEntry *var_type = const_val->data.x_bignum.is_negative ?
-                g->builtin_types.entry_isize : g->builtin_types.entry_usize;
-            LLVMValueRef init_val = LLVMConstInt(var_type->type_ref,
-                bignum_to_twos_complement(&const_val->data.x_bignum), false);
+            size_t bits_needed = bigint_bits_needed(&const_val->data.x_bigint);
+            TypeTableEntry *var_type = get_int_type(g, const_val->data.x_bigint.is_negative, bits_needed);
+            LLVMValueRef init_val = bigint_to_llvm_const(var_type->type_ref, &const_val->data.x_bigint);
             gen_global_var(g, var, init_val, var_type);
             continue;
         }
src/ir.cpp
@@ -656,16 +656,23 @@ static IrInstruction *ir_build_const_uint(IrBuilder *irb, Scope *scope, AstNode
     IrInstructionConst *const_instruction = ir_build_instruction<IrInstructionConst>(irb, scope, source_node);
     const_instruction->base.value.type = irb->codegen->builtin_types.entry_num_lit_int;
     const_instruction->base.value.special = ConstValSpecialStatic;
-    bignum_init_unsigned(&const_instruction->base.value.data.x_bignum, value);
+    bigint_init_unsigned(&const_instruction->base.value.data.x_bigint, value);
     return &const_instruction->base;
 }
 
-static IrInstruction *ir_build_const_bignum(IrBuilder *irb, Scope *scope, AstNode *source_node, BigNum *bignum) {
+static IrInstruction *ir_build_const_bigint(IrBuilder *irb, Scope *scope, AstNode *source_node, BigInt *bigint) {
     IrInstructionConst *const_instruction = ir_build_instruction<IrInstructionConst>(irb, scope, source_node);
-    const_instruction->base.value.type = (bignum->kind == BigNumKindInt) ?
-        irb->codegen->builtin_types.entry_num_lit_int : irb->codegen->builtin_types.entry_num_lit_float;
+    const_instruction->base.value.type = irb->codegen->builtin_types.entry_num_lit_int;
+    const_instruction->base.value.special = ConstValSpecialStatic;
+    bigint_init_bigint(&const_instruction->base.value.data.x_bigint, bigint);
+    return &const_instruction->base;
+}
+
+static IrInstruction *ir_build_const_bigfloat(IrBuilder *irb, Scope *scope, AstNode *source_node, BigFloat *bigfloat) {
+    IrInstructionConst *const_instruction = ir_build_instruction<IrInstructionConst>(irb, scope, source_node);
+    const_instruction->base.value.type = irb->codegen->builtin_types.entry_num_lit_float;
     const_instruction->base.value.special = ConstValSpecialStatic;
-    const_instruction->base.value.data.x_bignum = *bignum;
+    bigfloat_init_bigfloat(&const_instruction->base.value.data.x_bigfloat, bigfloat);
     return &const_instruction->base;
 }
 
@@ -680,7 +687,7 @@ static IrInstruction *ir_build_const_usize(IrBuilder *irb, Scope *scope, AstNode
     IrInstructionConst *const_instruction = ir_build_instruction<IrInstructionConst>(irb, scope, source_node);
     const_instruction->base.value.type = irb->codegen->builtin_types.entry_usize;
     const_instruction->base.value.special = ConstValSpecialStatic;
-    bignum_init_unsigned(&const_instruction->base.value.data.x_bignum, value);
+    bigint_init_unsigned(&const_instruction->base.value.data.x_bigint, value);
     return &const_instruction->base;
 }
 
@@ -3687,15 +3694,21 @@ static IrInstruction *ir_gen_bin_op(IrBuilder *irb, Scope *scope, AstNode *node)
     zig_unreachable();
 }
 
-static IrInstruction *ir_gen_num_lit(IrBuilder *irb, Scope *scope, AstNode *node) {
-    assert(node->type == NodeTypeNumberLiteral);
+static IrInstruction *ir_gen_int_lit(IrBuilder *irb, Scope *scope, AstNode *node) {
+    assert(node->type == NodeTypeIntLiteral);
+
+    return ir_build_const_bigint(irb, scope, node, node->data.int_literal.bigint);
+}
 
-    if (node->data.number_literal.overflow) {
-        add_node_error(irb->codegen, node, buf_sprintf("number literal too large to be represented in any type"));
+static IrInstruction *ir_gen_float_lit(IrBuilder *irb, Scope *scope, AstNode *node) {
+    assert(node->type == NodeTypeFloatLiteral);
+
+    if (node->data.float_literal.overflow) {
+        add_node_error(irb->codegen, node, buf_sprintf("float literal too large to be represented in any type"));
         return irb->codegen->invalid_instruction;
     }
 
-    return ir_build_const_bignum(irb, scope, node, node->data.number_literal.bignum);
+    return ir_build_const_bigfloat(irb, scope, node, node->data.float_literal.bigfloat);
 }
 
 static IrInstruction *ir_gen_char_lit(IrBuilder *irb, Scope *scope, AstNode *node) {
@@ -5933,8 +5946,10 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop
             return ir_gen_node_raw(irb, node->data.grouped_expr, scope, lval);
         case NodeTypeBinOpExpr:
             return ir_lval_wrap(irb, scope, ir_gen_bin_op(irb, scope, node), lval);
-        case NodeTypeNumberLiteral:
-            return ir_lval_wrap(irb, scope, ir_gen_num_lit(irb, scope, node), lval);
+        case NodeTypeIntLiteral:
+            return ir_lval_wrap(irb, scope, ir_gen_int_lit(irb, scope, node), lval);
+        case NodeTypeFloatLiteral:
+            return ir_lval_wrap(irb, scope, ir_gen_float_lit(irb, scope, node), lval);
         case NodeTypeCharLiteral:
             return ir_lval_wrap(irb, scope, ir_gen_char_lit(irb, scope, node), lval);
         case NodeTypeSymbol:
@@ -6184,6 +6199,13 @@ static bool ir_emit_global_runtime_side_effect(IrAnalyze *ira, IrInstruction *so
     return true;
 }
 
+static bool const_val_fits_in_num_lit(ConstExprValue *const_val, TypeTableEntry *num_lit_type) {
+    return ((num_lit_type->id == TypeTableEntryIdNumLitFloat &&
+        (const_val->type->id == TypeTableEntryIdFloat || const_val->type->id == TypeTableEntryIdNumLitFloat)) ||
+               (num_lit_type->id == TypeTableEntryIdNumLitInt &&
+        (const_val->type->id == TypeTableEntryIdInt || const_val->type->id == TypeTableEntryIdNumLitInt)));
+}
+
 static bool ir_num_lit_fits_in_other_type(IrAnalyze *ira, IrInstruction *instruction, TypeTableEntry *other_type) {
     if (type_is_invalid(other_type)) {
         return false;
@@ -6191,44 +6213,51 @@ static bool ir_num_lit_fits_in_other_type(IrAnalyze *ira, IrInstruction *instruc
 
     ConstExprValue *const_val = &instruction->value;
     assert(const_val->special != ConstValSpecialRuntime);
+
+    bool const_val_is_int = (const_val->type->id == TypeTableEntryIdInt ||
+            const_val->type->id == TypeTableEntryIdNumLitInt);
+    bool const_val_is_float = (const_val->type->id == TypeTableEntryIdFloat ||
+            const_val->type->id == TypeTableEntryIdNumLitFloat);
     if (other_type->id == TypeTableEntryIdFloat) {
         return true;
-    } else if (other_type->id == TypeTableEntryIdInt &&
-               const_val->data.x_bignum.kind == BigNumKindInt)
-    {
-        if (bignum_fits_in_bits(&const_val->data.x_bignum, other_type->data.integral.bit_count,
+    } else if (other_type->id == TypeTableEntryIdInt && const_val_is_int) {
+        if (bigint_fits_in_bits(&const_val->data.x_bigint, other_type->data.integral.bit_count,
                     other_type->data.integral.is_signed))
         {
             return true;
         }
-    } else if ((other_type->id == TypeTableEntryIdNumLitFloat && const_val->data.x_bignum.kind == BigNumKindFloat) ||
-               (other_type->id == TypeTableEntryIdNumLitInt   && const_val->data.x_bignum.kind == BigNumKindInt  ))
-    {
+    } else if (const_val_fits_in_num_lit(const_val, other_type)) {
         return true;
     } else if (other_type->id == TypeTableEntryIdMaybe) {
         TypeTableEntry *child_type = other_type->data.maybe.child_type;
-        if ((child_type->id == TypeTableEntryIdNumLitFloat && const_val->data.x_bignum.kind == BigNumKindFloat) ||
-            (child_type->id == TypeTableEntryIdNumLitInt   && const_val->data.x_bignum.kind == BigNumKindInt  ))
-        {
+        if (const_val_fits_in_num_lit(const_val, child_type)) {
             return true;
-        } else if (child_type->id == TypeTableEntryIdInt && const_val->data.x_bignum.kind == BigNumKindInt) {
-            if (bignum_fits_in_bits(&const_val->data.x_bignum,
+        } else if (child_type->id == TypeTableEntryIdInt && const_val_is_int) {
+            if (bigint_fits_in_bits(&const_val->data.x_bigint,
                         child_type->data.integral.bit_count,
                         child_type->data.integral.is_signed))
             {
                 return true;
             }
-        } else if (child_type->id == TypeTableEntryIdFloat && const_val->data.x_bignum.kind == BigNumKindFloat) {
+        } else if (child_type->id == TypeTableEntryIdFloat && const_val_is_float) {
             return true;
         }
     }
 
-    const char *num_lit_str = (const_val->data.x_bignum.kind == BigNumKindFloat) ? "float" : "integer";
+    const char *num_lit_str;
+    Buf *val_buf = buf_alloc();
+    if (const_val_is_float) {
+        num_lit_str = "float";
+        bigfloat_write_buf(val_buf, &const_val->data.x_bigfloat);
+    } else {
+        num_lit_str = "integer";
+        bigint_write_buf(val_buf, &const_val->data.x_bigint, 10);
+    }
 
     ir_add_error(ira, instruction,
         buf_sprintf("%s value %s cannot be implicitly casted to type '%s'",
             num_lit_str,
-            buf_ptr(bignum_to_buf(&const_val->data.x_bignum)),
+            buf_ptr(val_buf),
             buf_ptr(&other_type->name)));
     return false;
 }
@@ -6643,7 +6672,13 @@ static void eval_const_expr_implicit_cast(CastOp cast_op,
                 break;
             }
         case CastOpNumLitToConcrete:
-            const_val->data.x_bignum = other_val->data.x_bignum;
+            if (other_val->type->id == TypeTableEntryIdNumLitFloat) {
+                bigfloat_init_bigfloat(&const_val->data.x_bigfloat, &other_val->data.x_bigfloat);
+            } else if (other_val->type->id == TypeTableEntryIdNumLitInt) {
+                bigint_init_bigint(&const_val->data.x_bigint, &other_val->data.x_bigint);
+            } else {
+                zig_unreachable();
+            }
             const_val->type = new_type;
             break;
         case CastOpResizeSlice:
@@ -6651,15 +6686,15 @@ static void eval_const_expr_implicit_cast(CastOp cast_op,
             // can't do it
             break;
         case CastOpIntToFloat:
-            bignum_cast_to_float(&const_val->data.x_bignum, &other_val->data.x_bignum);
+            bigfloat_init_bigint(&const_val->data.x_bigfloat, &other_val->data.x_bigint);
             const_val->special = ConstValSpecialStatic;
             break;
         case CastOpFloatToInt:
-            bignum_cast_to_int(&const_val->data.x_bignum, &other_val->data.x_bignum);
+            bigint_init_bigfloat(&const_val->data.x_bigint, &other_val->data.x_bigfloat);
             const_val->special = ConstValSpecialStatic;
             break;
         case CastOpBoolToInt:
-            bignum_init_unsigned(&const_val->data.x_bignum, other_val->data.x_bool ? 1 : 0);
+            bigint_init_unsigned(&const_val->data.x_bigint, other_val->data.x_bool ? 1 : 0);
             const_val->special = ConstValSpecialStatic;
             break;
     }
@@ -6878,7 +6913,7 @@ static TypeTableEntry *ir_analyze_const_ptr(IrAnalyze *ira, IrInstruction *instr
 
 static TypeTableEntry *ir_analyze_const_usize(IrAnalyze *ira, IrInstruction *instruction, uint64_t value) {
     ConstExprValue *const_val = ir_build_const_from(ira, instruction);
-    bignum_init_unsigned(&const_val->data.x_bignum, value);
+    bigint_init_unsigned(&const_val->data.x_bigint, value);
     return ira->codegen->builtin_types.entry_usize;
 }
 
@@ -7239,12 +7274,12 @@ static IrInstruction *ir_analyze_widen_or_shorten(IrAnalyze *ira, IrInstruction
         if (!val)
             return ira->codegen->invalid_instruction;
         if (wanted_type->id == TypeTableEntryIdInt) {
-            if (val->data.x_bignum.is_negative && !wanted_type->data.integral.is_signed) {
+            if (bigint_cmp_zero(&val->data.x_bigint) == CmpLT && !wanted_type->data.integral.is_signed) {
                 ir_add_error(ira, source_instr,
                     buf_sprintf("attempt to cast negative value to unsigned integer"));
                 return ira->codegen->invalid_instruction;
             }
-            if (!bignum_fits_in_bits(&val->data.x_bignum, wanted_type->data.integral.bit_count,
+            if (!bigint_fits_in_bits(&val->data.x_bigint, wanted_type->data.integral.bit_count,
                     wanted_type->data.integral.is_signed))
             {
                 ir_add_error(ira, source_instr,
@@ -7255,7 +7290,11 @@ static IrInstruction *ir_analyze_widen_or_shorten(IrAnalyze *ira, IrInstruction
         }
         IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope,
                 source_instr->source_node, wanted_type);
-        result->value.data.x_bignum = val->data.x_bignum;
+        if (wanted_type->id == TypeTableEntryIdInt) {
+            bigint_init_bigint(&result->value.data.x_bigint, &val->data.x_bigint);
+        } else {
+            bigfloat_init_bigfloat(&result->value.data.x_bigfloat, &val->data.x_bigfloat);
+        }
         result->value.type = wanted_type;
         return result;
     }
@@ -7278,7 +7317,7 @@ static IrInstruction *ir_analyze_ptr_to_int(IrAnalyze *ira, IrInstruction *sourc
         if (val->data.x_ptr.special == ConstPtrSpecialHardCodedAddr) {
             IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope,
                     source_instr->source_node, wanted_type);
-            bignum_init_unsigned(&result->value.data.x_bignum, val->data.x_ptr.data.hard_coded_addr.addr);
+            bigint_init_unsigned(&result->value.data.x_bigint, val->data.x_ptr.data.hard_coded_addr.addr);
             return result;
         }
     }
@@ -7299,9 +7338,20 @@ static IrInstruction *ir_analyze_int_to_enum(IrAnalyze *ira, IrInstruction *sour
         ConstExprValue *val = ir_resolve_const(ira, target, UndefBad);
         if (!val)
             return ira->codegen->invalid_instruction;
+        BigInt enum_member_count;
+        bigint_init_unsigned(&enum_member_count, wanted_type->data.enumeration.src_field_count);
+        if (bigint_cmp(&val->data.x_bigint, &enum_member_count) != CmpLT) {
+            Buf *val_buf = buf_alloc();
+            bigint_write_buf(val_buf, &val->data.x_bigint, 10);
+            ir_add_error(ira, source_instr,
+                buf_sprintf("integer value %s too big for enum '%s' which has %" PRIu32 " fields",
+                    buf_ptr(val_buf), buf_ptr(&wanted_type->name), wanted_type->data.enumeration.src_field_count));
+            return ira->codegen->invalid_instruction;
+        }
+
         IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope,
                 source_instr->source_node, wanted_type);
-        result->value.data.x_enum.tag = val->data.x_bignum.data.x_uint;
+        result->value.data.x_enum.tag = bigint_as_unsigned(&val->data.x_bigint);
         return result;
     }
 
@@ -7320,7 +7370,13 @@ static IrInstruction *ir_analyze_number_to_literal(IrAnalyze *ira, IrInstruction
 
     IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope,
             source_instr->source_node, wanted_type);
-    bignum_init_bignum(&result->value.data.x_bignum, &val->data.x_bignum);
+    if (wanted_type->id == TypeTableEntryIdNumLitFloat) {
+        bigfloat_init_bigfloat(&result->value.data.x_bigfloat, &val->data.x_bigfloat);
+    } else if (wanted_type->id == TypeTableEntryIdNumLitInt) {
+        bigint_init_bigint(&result->value.data.x_bigint, &val->data.x_bigint);
+    } else {
+        zig_unreachable();
+    }
     return result;
 }
 
@@ -7336,13 +7392,17 @@ static IrInstruction *ir_analyze_int_to_err(IrAnalyze *ira, IrInstruction *sourc
         IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope,
                 source_instr->source_node, ira->codegen->builtin_types.entry_pure_error);
 
-        uint64_t index = val->data.x_bignum.data.x_uint;
-        if (index == 0 || index >= ira->codegen->error_decls.length) {
+        BigInt err_count;
+        bigint_init_unsigned(&err_count, ira->codegen->error_decls.length);
+        if (bigint_cmp_zero(&val->data.x_bigint) == CmpEQ || bigint_cmp(&val->data.x_bigint, &err_count) != CmpLT) {
+            Buf *val_buf = buf_alloc();
+            bigint_write_buf(val_buf, &val->data.x_bigint, 10);
             ir_add_error(ira, source_instr,
-                buf_sprintf("integer value %" ZIG_PRI_u64 " represents no error", index));
+                buf_sprintf("integer value %s represents no error", buf_ptr(val_buf)));
             return ira->codegen->invalid_instruction;
         }
 
+        size_t index = bigint_as_unsigned(&val->data.x_bigint);
         AstNode *error_decl_node = ira->codegen->error_decls.at(index);
         result->value.data.x_pure_err = error_decl_node->data.error_value_decl.err;
         return result;
@@ -7378,9 +7438,9 @@ static IrInstruction *ir_analyze_err_to_int(IrAnalyze *ira, IrInstruction *sourc
         }
         result->value.type = wanted_type;
         uint64_t err_value = err ? err->value : 0;
-        bignum_init_unsigned(&result->value.data.x_bignum, err_value);
+        bigint_init_unsigned(&result->value.data.x_bigint, err_value);
 
-        if (!bignum_fits_in_bits(&result->value.data.x_bignum,
+        if (!bigint_fits_in_bits(&result->value.data.x_bigint,
             wanted_type->data.integral.bit_count, wanted_type->data.integral.is_signed))
         {
             ir_add_error_node(ira, source_instr->source_node,
@@ -7392,9 +7452,9 @@ static IrInstruction *ir_analyze_err_to_int(IrAnalyze *ira, IrInstruction *sourc
         return result;
     }
 
-    BigNum bn;
-    bignum_init_unsigned(&bn, ira->codegen->error_decls.length);
-    if (!bignum_fits_in_bits(&bn, wanted_type->data.integral.bit_count, wanted_type->data.integral.is_signed)) {
+    BigInt bn;
+    bigint_init_unsigned(&bn, ira->codegen->error_decls.length);
+    if (!bigint_fits_in_bits(&bn, wanted_type->data.integral.bit_count, wanted_type->data.integral.is_signed)) {
         ir_add_error_node(ira, source_instr->source_node,
                 buf_sprintf("too many error values to fit in '%s'", buf_ptr(&wanted_type->name)));
         return ira->codegen->invalid_instruction;
@@ -7861,7 +7921,7 @@ static bool ir_resolve_usize(IrAnalyze *ira, IrInstruction *value, uint64_t *out
     if (!const_val)
         return false;
 
-    *out = const_val->data.x_bignum.data.x_uint;
+    *out = bigint_as_unsigned(&const_val->data.x_bigint);
     return true;
 }
 
@@ -7941,7 +8001,7 @@ static Buf *ir_resolve_str(IrAnalyze *ira, IrInstruction *value) {
     assert(ptr_field->data.x_ptr.special == ConstPtrSpecialBaseArray);
     ConstExprValue *array_val = ptr_field->data.x_ptr.data.base_array.array_val;
     expand_undef_array(ira->codegen, array_val);
-    size_t len = len_field->data.x_bignum.data.x_uint;
+    size_t len = bigint_as_unsigned(&len_field->data.x_bigint);
     Buf *result = buf_alloc();
     buf_resize(result, len);
     for (size_t i = 0; i < len; i += 1) {
@@ -7951,7 +8011,7 @@ static Buf *ir_resolve_str(IrAnalyze *ira, IrInstruction *value) {
             ir_add_error(ira, casted_value, buf_sprintf("use of undefined value"));
             return nullptr;
         }
-        uint64_t big_c = char_val->data.x_bignum.data.x_uint;
+        uint64_t big_c = bigint_as_unsigned(&char_val->data.x_bigint);
         assert(big_c <= UINT8_MAX);
         uint8_t c = (uint8_t)big_c;
         buf_ptr(result)[i] = c;
@@ -8039,6 +8099,24 @@ static TypeTableEntry *ir_analyze_bin_op_bool(IrAnalyze *ira, IrInstructionBinOp
     return bool_type;
 }
 
+static bool resolve_cmp_op_id(IrBinOp op_id, Cmp cmp) {
+    if (op_id == IrBinOpCmpEq) {
+        return cmp == CmpEQ;
+    } else if (op_id == IrBinOpCmpNotEq) {
+        return cmp != CmpEQ;
+    } else if (op_id == IrBinOpCmpLessThan) {
+        return cmp == CmpLT;
+    } else if (op_id == IrBinOpCmpGreaterThan) {
+        return cmp == CmpGT;
+    } else if (op_id == IrBinOpCmpLessOrEq) {
+        return cmp != CmpGT;
+    } else if (op_id == IrBinOpCmpGreaterOrEq) {
+        return cmp != CmpLT;
+    } else {
+        zig_unreachable();
+    }
+}
+
 static TypeTableEntry *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp *bin_op_instruction) {
     IrInstruction *op1 = bin_op_instruction->op1->other;
     IrInstruction *op2 = bin_op_instruction->op2->other;
@@ -8157,30 +8235,13 @@ static TypeTableEntry *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp
     ConstExprValue *op1_val = &casted_op1->value;
     ConstExprValue *op2_val = &casted_op2->value;
     if ((value_is_comptime(op1_val) && value_is_comptime(op2_val)) || resolved_type->id == TypeTableEntryIdVoid) {
-        bool type_can_gt_lt_cmp = (resolved_type->id == TypeTableEntryIdNumLitFloat ||
-                resolved_type->id == TypeTableEntryIdNumLitInt ||
-                resolved_type->id == TypeTableEntryIdFloat ||
-                resolved_type->id == TypeTableEntryIdInt);
         bool answer;
-        if (type_can_gt_lt_cmp) {
-            bool (*bignum_cmp)(BigNum *, BigNum *);
-            if (op_id == IrBinOpCmpEq) {
-                bignum_cmp = bignum_cmp_eq;
-            } else if (op_id == IrBinOpCmpNotEq) {
-                bignum_cmp = bignum_cmp_neq;
-            } else if (op_id == IrBinOpCmpLessThan) {
-                bignum_cmp = bignum_cmp_lt;
-            } else if (op_id == IrBinOpCmpGreaterThan) {
-                bignum_cmp = bignum_cmp_gt;
-            } else if (op_id == IrBinOpCmpLessOrEq) {
-                bignum_cmp = bignum_cmp_lte;
-            } else if (op_id == IrBinOpCmpGreaterOrEq) {
-                bignum_cmp = bignum_cmp_gte;
-            } else {
-                zig_unreachable();
-            }
-
-            answer = bignum_cmp(&op1_val->data.x_bignum, &op2_val->data.x_bignum);
+        if (resolved_type->id == TypeTableEntryIdNumLitFloat || resolved_type->id == TypeTableEntryIdFloat) {
+            Cmp cmp_result = bigfloat_cmp(&op1_val->data.x_bigfloat, &op2_val->data.x_bigfloat);
+            answer = resolve_cmp_op_id(op_id, cmp_result);
+        } else if (resolved_type->id == TypeTableEntryIdNumLitInt || resolved_type->id == TypeTableEntryIdInt) {
+            Cmp cmp_result = bigint_cmp(&op1_val->data.x_bigint, &op2_val->data.x_bigint);
+            answer = resolve_cmp_op_id(op_id, cmp_result);
         } else {
             bool are_equal = resolved_type->id == TypeTableEntryIdVoid || const_values_equal(op1_val, op2_val);
             if (op_id == IrBinOpCmpEq) {
@@ -8220,7 +8281,7 @@ static TypeTableEntry *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp
         } else {
             known_left_val = nullptr;
         }
-        if (known_left_val != nullptr && known_left_val->data.x_bignum.data.x_uint == 0 &&
+        if (known_left_val != nullptr && bigint_cmp_zero(&known_left_val->data.x_bigint) == CmpEQ &&
             (flipped_op_id == IrBinOpCmpLessOrEq || flipped_op_id == IrBinOpCmpGreaterThan))
         {
             bool answer = (flipped_op_id == IrBinOpCmpLessOrEq);
@@ -8236,101 +8297,35 @@ static TypeTableEntry *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp
     return ira->codegen->builtin_types.entry_bool;
 }
 
-enum EvalBigNumSpecial {
-    EvalBigNumSpecialNone,
-    EvalBigNumSpecialWrapping,
-    EvalBigNumSpecialExact,
-};
-
-static int ir_eval_bignum(ConstExprValue *op1_val, ConstExprValue *op2_val,
-        ConstExprValue *out_val, bool (*bignum_fn)(BigNum *, BigNum *, BigNum *),
-        TypeTableEntry *type, EvalBigNumSpecial special)
+static int ir_eval_math_op(TypeTableEntry *type_entry, ConstExprValue *op1_val,
+        IrBinOp op_id, ConstExprValue *op2_val, ConstExprValue *out_val)
 {
-    bool is_int = false;
-    bool is_float = false;
-    if (type->id == TypeTableEntryIdInt ||
-        type->id == TypeTableEntryIdNumLitInt)
-    {
+    bool is_int;
+    bool is_float;
+    Cmp op2_zcmp;
+    if (type_entry->id == TypeTableEntryIdInt || type_entry->id == TypeTableEntryIdNumLitInt) {
         is_int = true;
-    } else if (type->id == TypeTableEntryIdFloat ||
-                type->id == TypeTableEntryIdNumLitFloat)
+        is_float = false;
+        op2_zcmp = bigint_cmp_zero(&op2_val->data.x_bigint);
+    } else if (type_entry->id == TypeTableEntryIdFloat ||
+                type_entry->id == TypeTableEntryIdNumLitFloat)
     {
+        is_int = false;
         is_float = true;
+        op2_zcmp = bigfloat_cmp_zero(&op2_val->data.x_bigfloat);
     } else {
         zig_unreachable();
     }
-    if (bignum_fn == bignum_div || bignum_fn == bignum_rem || bignum_fn == bignum_mod ||
-        bignum_fn == bignum_div_trunc || bignum_fn == bignum_div_floor)
-    {
-        if ((is_int && op2_val->data.x_bignum.data.x_uint == 0) ||
-            (is_float && op2_val->data.x_bignum.data.x_float == 0.0))
-        {
-            return ErrorDivByZero;
-        }
-    }
-    if (bignum_fn == bignum_rem || bignum_fn == bignum_mod) {
-        BigNum zero;
-        if (is_float) {
-            bignum_init_float(&zero, 0.0);
-        } else {
-            bignum_init_unsigned(&zero, 0);
-        }
-        if (bignum_cmp_lt(&op2_val->data.x_bignum, &zero)) {
-            return ErrorNegativeDenominator;
-        }
-    }
-
-    if (special == EvalBigNumSpecialExact) {
-        assert(bignum_fn == bignum_div);
-        BigNum remainder;
-        if (bignum_rem(&remainder, &op1_val->data.x_bignum, &op2_val->data.x_bignum)) {
-            return ErrorOverflow;
-        }
-        BigNum zero;
-        if (is_float) {
-            bignum_init_float(&zero, 0.0);
-        } else {
-            bignum_init_unsigned(&zero, 0);
-        }
-        if (bignum_cmp_neq(&remainder, &zero)) {
-            return ErrorExactDivRemainder;
-        }
-    }
-
-    bool overflow = bignum_fn(&out_val->data.x_bignum, &op1_val->data.x_bignum, &op2_val->data.x_bignum);
-    if (overflow) {
-        if (special == EvalBigNumSpecialWrapping) {
-            zig_panic("TODO compiler bug, implement compile-time wrapping arithmetic for >= 64 bit ints");
-        } else {
-            return ErrorOverflow;
-        }
-    }
 
-    if (type->id == TypeTableEntryIdInt && !bignum_fits_in_bits(&out_val->data.x_bignum,
-                type->data.integral.bit_count, type->data.integral.is_signed))
+    if ((op_id == IrBinOpDivUnspecified || op_id == IrBinOpRemRem || op_id == IrBinOpRemMod ||
+        op_id == IrBinOpDivTrunc || op_id == IrBinOpDivFloor) && op2_zcmp == CmpEQ)
     {
-        if (special == EvalBigNumSpecialWrapping) {
-            if (type->data.integral.is_signed) {
-                out_val->data.x_bignum.data.x_uint = max_unsigned_val(type) - out_val->data.x_bignum.data.x_uint + 1;
-                out_val->data.x_bignum.is_negative = !out_val->data.x_bignum.is_negative;
-            } else if (out_val->data.x_bignum.is_negative) {
-                out_val->data.x_bignum.data.x_uint = max_unsigned_val(type) - out_val->data.x_bignum.data.x_uint + 1;
-                out_val->data.x_bignum.is_negative = false;
-            } else {
-                bignum_truncate(&out_val->data.x_bignum, type->data.integral.bit_count);
-            }
-        } else {
-            return ErrorOverflow;
-        }
+        return ErrorDivByZero;
+    }
+    if ((op_id == IrBinOpRemRem || op_id == IrBinOpRemMod) && op2_zcmp == CmpLT) {
+        return ErrorNegativeDenominator;
     }
 
-    out_val->special = ConstValSpecialStatic;
-    return 0;
-}
-
-static int ir_eval_math_op(TypeTableEntry *canon_type, ConstExprValue *op1_val,
-        IrBinOp op_id, ConstExprValue *op2_val, ConstExprValue *out_val)
-{
     switch (op_id) {
         case IrBinOpInvalid:
         case IrBinOpBoolOr:
@@ -8346,43 +8341,128 @@ static int ir_eval_math_op(TypeTableEntry *canon_type, ConstExprValue *op1_val,
         case IrBinOpRemUnspecified:
             zig_unreachable();
         case IrBinOpBinOr:
-            return ir_eval_bignum(op1_val, op2_val, out_val, bignum_or, canon_type, EvalBigNumSpecialNone);
+            assert(is_int);
+            bigint_or(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint);
+            break;
         case IrBinOpBinXor:
-            return ir_eval_bignum(op1_val, op2_val, out_val, bignum_xor, canon_type, EvalBigNumSpecialNone);
+            assert(is_int);
+            bigint_xor(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint);
+            break;
         case IrBinOpBinAnd:
-            return ir_eval_bignum(op1_val, op2_val, out_val, bignum_and, canon_type, EvalBigNumSpecialNone);
+            assert(is_int);
+            bigint_and(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint);
+            break;
         case IrBinOpBitShiftLeft:
-            return ir_eval_bignum(op1_val, op2_val, out_val, bignum_shl, canon_type, EvalBigNumSpecialNone);
+            assert(is_int);
+            bigint_shl(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint);
+            break;
         case IrBinOpBitShiftLeftWrap:
-            return ir_eval_bignum(op1_val, op2_val, out_val, bignum_shl, canon_type, EvalBigNumSpecialWrapping);
+            assert(type_entry->id == TypeTableEntryIdInt);
+            bigint_shl_wrap(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint,
+                    type_entry->data.integral.bit_count, type_entry->data.integral.is_signed);
+            break;
         case IrBinOpBitShiftRight:
-            return ir_eval_bignum(op1_val, op2_val, out_val, bignum_shr, canon_type, EvalBigNumSpecialNone);
+            assert(is_int);
+            bigint_shr(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint);
+            break;
         case IrBinOpAdd:
-            return ir_eval_bignum(op1_val, op2_val, out_val, bignum_add, canon_type, EvalBigNumSpecialNone);
+            if (is_int) {
+                bigint_add(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint);
+            } else {
+                bigfloat_add(&out_val->data.x_bigfloat, &op1_val->data.x_bigfloat, &op2_val->data.x_bigfloat);
+            }
+            break;
         case IrBinOpAddWrap:
-            return ir_eval_bignum(op1_val, op2_val, out_val, bignum_add, canon_type, EvalBigNumSpecialWrapping);
+            assert(type_entry->id == TypeTableEntryIdInt);
+            bigint_add_wrap(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint,
+                    type_entry->data.integral.bit_count, type_entry->data.integral.is_signed);
+            break;
         case IrBinOpSub:
-            return ir_eval_bignum(op1_val, op2_val, out_val, bignum_sub, canon_type, EvalBigNumSpecialNone);
+            if (is_int) {
+                bigint_sub(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint);
+            } else {
+                bigfloat_sub(&out_val->data.x_bigfloat, &op1_val->data.x_bigfloat, &op2_val->data.x_bigfloat);
+            }
+            break;
         case IrBinOpSubWrap:
-            return ir_eval_bignum(op1_val, op2_val, out_val, bignum_sub, canon_type, EvalBigNumSpecialWrapping);
+            assert(type_entry->id == TypeTableEntryIdInt);
+            bigint_sub_wrap(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint,
+                    type_entry->data.integral.bit_count, type_entry->data.integral.is_signed);
+            break;
         case IrBinOpMult:
-            return ir_eval_bignum(op1_val, op2_val, out_val, bignum_mul, canon_type, EvalBigNumSpecialNone);
+            if (is_int) {
+                bigint_mul(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint);
+            } else {
+                bigfloat_mul(&out_val->data.x_bigfloat, &op1_val->data.x_bigfloat, &op2_val->data.x_bigfloat);
+            }
+            break;
         case IrBinOpMultWrap:
-            return ir_eval_bignum(op1_val, op2_val, out_val, bignum_mul, canon_type, EvalBigNumSpecialWrapping);
+            assert(type_entry->id == TypeTableEntryIdInt);
+            bigint_mul_wrap(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint,
+                    type_entry->data.integral.bit_count, type_entry->data.integral.is_signed);
+            break;
         case IrBinOpDivUnspecified:
-            return ir_eval_bignum(op1_val, op2_val, out_val, bignum_div, canon_type, EvalBigNumSpecialNone);
+            assert(is_float);
+            bigfloat_div(&out_val->data.x_bigfloat, &op1_val->data.x_bigfloat, &op2_val->data.x_bigfloat);
+            break;
         case IrBinOpDivTrunc:
-            return ir_eval_bignum(op1_val, op2_val, out_val, bignum_div_trunc, canon_type, EvalBigNumSpecialNone);
+            if (is_int) {
+                bigint_div_trunc(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint);
+            } else {
+                bigfloat_div_trunc(&out_val->data.x_bigfloat, &op1_val->data.x_bigfloat, &op2_val->data.x_bigfloat);
+            }
+            break;
         case IrBinOpDivFloor:
-            return ir_eval_bignum(op1_val, op2_val, out_val, bignum_div_floor, canon_type, EvalBigNumSpecialNone);
+            if (is_int) {
+                bigint_div_floor(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint);
+            } else {
+                bigfloat_div_floor(&out_val->data.x_bigfloat, &op1_val->data.x_bigfloat, &op2_val->data.x_bigfloat);
+            }
+            break;
         case IrBinOpDivExact:
-            return ir_eval_bignum(op1_val, op2_val, out_val, bignum_div, canon_type, EvalBigNumSpecialExact);
+            if (is_int) {
+                bigint_div_trunc(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint);
+                BigInt remainder;
+                bigint_rem(&remainder, &op1_val->data.x_bigint, &op2_val->data.x_bigint);
+                if (bigint_cmp_zero(&remainder) != CmpEQ) {
+                    return ErrorExactDivRemainder;
+                }
+            } else {
+                bigfloat_div_trunc(&out_val->data.x_bigfloat, &op1_val->data.x_bigfloat, &op2_val->data.x_bigfloat);
+                BigFloat remainder;
+                bigfloat_rem(&remainder, &op1_val->data.x_bigfloat, &op2_val->data.x_bigfloat);
+                if (bigfloat_cmp_zero(&remainder) != CmpEQ) {
+                    return ErrorExactDivRemainder;
+                }
+            }
+            break;
         case IrBinOpRemRem:
-            return ir_eval_bignum(op1_val, op2_val, out_val, bignum_rem, canon_type, EvalBigNumSpecialNone);
+            if (is_int) {
+                bigint_rem(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint);
+            } else {
+                bigfloat_rem(&out_val->data.x_bigfloat, &op1_val->data.x_bigfloat, &op2_val->data.x_bigfloat);
+            }
+            break;
         case IrBinOpRemMod:
-            return ir_eval_bignum(op1_val, op2_val, out_val, bignum_mod, canon_type, EvalBigNumSpecialNone);
+            if (is_int) {
+                bigint_mod(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint);
+            } else {
+                bigfloat_mod(&out_val->data.x_bigfloat, &op1_val->data.x_bigfloat, &op2_val->data.x_bigfloat);
+            }
+            break;
     }
-    zig_unreachable();
+
+    if (type_entry->id == TypeTableEntryIdInt) {
+        if (!bigint_fits_in_bits(&out_val->data.x_bigint, type_entry->data.integral.bit_count,
+                type_entry->data.integral.is_signed))
+        {
+            return ErrorOverflow;
+        }
+    }
+
+    out_val->type = type_entry;
+    out_val->special = ConstValSpecialStatic;
+    return 0;
 }
 
 static TypeTableEntry *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstructionBinOp *bin_op_instruction) {
@@ -8395,31 +8475,32 @@ static TypeTableEntry *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstructionBinOp
     IrBinOp op_id = bin_op_instruction->op_id;
 
     bool is_int = resolved_type->id == TypeTableEntryIdInt || resolved_type->id == TypeTableEntryIdNumLitInt;
-    bool is_signed = ((resolved_type->id == TypeTableEntryIdInt && resolved_type->data.integral.is_signed) ||
-            resolved_type->id == TypeTableEntryIdFloat ||
-            (resolved_type->id == TypeTableEntryIdNumLitFloat &&
-                (op1->value.data.x_bignum.data.x_float < 0.0 || op2->value.data.x_bignum.data.x_float < 0.0)) ||
-            (resolved_type->id == TypeTableEntryIdNumLitInt &&
-                (op1->value.data.x_bignum.is_negative || op2->value.data.x_bignum.is_negative)));
-    if (op_id == IrBinOpDivUnspecified) {
-        if (is_int && is_signed) {
+    bool is_float = resolved_type->id == TypeTableEntryIdFloat || resolved_type->id == TypeTableEntryIdNumLitFloat;
+    bool is_signed_div = (
+        (resolved_type->id == TypeTableEntryIdInt && resolved_type->data.integral.is_signed) ||
+        resolved_type->id == TypeTableEntryIdFloat ||
+        (resolved_type->id == TypeTableEntryIdNumLitFloat &&
+            ((bigfloat_cmp_zero(&op1->value.data.x_bigfloat) != CmpGT) !=
+             (bigfloat_cmp_zero(&op2->value.data.x_bigfloat) != CmpGT))) ||
+        (resolved_type->id == TypeTableEntryIdNumLitInt &&
+            ((bigint_cmp_zero(&op1->value.data.x_bigint) != CmpGT) !=
+             (bigint_cmp_zero(&op2->value.data.x_bigint) != CmpGT)))
+    );
+    if (op_id == IrBinOpDivUnspecified && is_int) {
+        if (is_signed_div) {
             bool ok = false;
             if (instr_is_comptime(op1) && instr_is_comptime(op2)) {
-				if (op2->value.data.x_bignum.data.x_uint == 0) {
+				if (bigint_cmp_zero(&op2->value.data.x_bigint) == CmpEQ) {
                     // the division by zero error will be caught later, but we don't have a
                     // division function ambiguity problem.
                     op_id = IrBinOpDivTrunc;
 					ok = true;
 				} else {
-					BigNum trunc_result;
-					BigNum floor_result;
-					if (bignum_div_trunc(&trunc_result, &op1->value.data.x_bignum, &op2->value.data.x_bignum)) {
-						zig_unreachable();
-					}
-					if (bignum_div_floor(&floor_result, &op1->value.data.x_bignum, &op2->value.data.x_bignum)) {
-						zig_unreachable();
-					}
-					if (bignum_cmp_eq(&trunc_result, &floor_result)) {
+					BigInt trunc_result;
+					BigInt floor_result;
+					bigint_div_trunc(&trunc_result, &op1->value.data.x_bigint, &op2->value.data.x_bigint);
+					bigint_div_floor(&floor_result, &op1->value.data.x_bigint, &op2->value.data.x_bigint);
+					if (bigint_cmp(&trunc_result, &floor_result) == CmpEQ) {
 						ok = true;
 						op_id = IrBinOpDivTrunc;
 					}
@@ -8432,29 +8513,37 @@ static TypeTableEntry *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstructionBinOp
                         buf_ptr(&op2->value.type->name)));
                 return ira->codegen->builtin_types.entry_invalid;
             }
-        } else if (is_int) {
+        } else {
             op_id = IrBinOpDivTrunc;
         }
     } else if (op_id == IrBinOpRemUnspecified) {
-        if (is_signed) {
+        if (is_signed_div && (is_int || is_float)) {
             bool ok = false;
             if (instr_is_comptime(op1) && instr_is_comptime(op2)) {
-                if ((is_int && op2->value.data.x_bignum.data.x_uint == 0) ||
-                    (!is_int && op2->value.data.x_bignum.data.x_float == 0.0))
-                {
-                    // the division by zero error will be caught later, but we don't
-                    // have a remainder function ambiguity problem
-                    ok = true;
-                } else {
-                    BigNum rem_result;
-                    BigNum mod_result;
-                    if (bignum_rem(&rem_result, &op1->value.data.x_bignum, &op2->value.data.x_bignum)) {
-                        zig_unreachable();
+                if (is_int) {
+                    if (bigint_cmp_zero(&op2->value.data.x_bigint) == CmpEQ) {
+                        // the division by zero error will be caught later, but we don't
+                        // have a remainder function ambiguity problem
+                        ok = true;
+                    } else {
+                        BigInt rem_result;
+                        BigInt mod_result;
+                        bigint_rem(&rem_result, &op1->value.data.x_bigint, &op2->value.data.x_bigint);
+                        bigint_mod(&mod_result, &op1->value.data.x_bigint, &op2->value.data.x_bigint);
+                        ok = bigint_cmp(&rem_result, &mod_result) == CmpEQ;
                     }
-                    if (bignum_mod(&mod_result, &op1->value.data.x_bignum, &op2->value.data.x_bignum)) {
-                        zig_unreachable();
+                } else {
+                    if (bigfloat_cmp_zero(&op2->value.data.x_bigfloat) == CmpEQ) {
+                        // the division by zero error will be caught later, but we don't
+                        // have a remainder function ambiguity problem
+                        ok = true;
+                    } else {
+                        BigFloat rem_result;
+                        BigFloat mod_result;
+                        bigfloat_rem(&rem_result, &op1->value.data.x_bigfloat, &op2->value.data.x_bigfloat);
+                        bigfloat_mod(&mod_result, &op1->value.data.x_bigfloat, &op2->value.data.x_bigfloat);
+                        ok = bigfloat_cmp(&rem_result, &mod_result) == CmpEQ;
                     }
-                    ok = bignum_cmp_eq(&rem_result, &mod_result);
                 }
             }
             if (!ok) {
@@ -8468,21 +8557,18 @@ static TypeTableEntry *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstructionBinOp
         op_id = IrBinOpRemRem;
     }
 
-    if (resolved_type->id == TypeTableEntryIdInt ||
-        resolved_type->id == TypeTableEntryIdNumLitInt)
-    {
+    if (is_int) {
         // int
-    } else if ((resolved_type->id == TypeTableEntryIdFloat ||
-                resolved_type->id == TypeTableEntryIdNumLitFloat) &&
+    } else if (is_float &&
         (op_id == IrBinOpAdd ||
-            op_id == IrBinOpSub ||
-            op_id == IrBinOpMult ||
-            op_id == IrBinOpDivUnspecified ||
-            op_id == IrBinOpDivTrunc ||
-            op_id == IrBinOpDivFloor ||
-            op_id == IrBinOpDivExact ||
-            op_id == IrBinOpRemRem ||
-            op_id == IrBinOpRemMod))
+        op_id == IrBinOpSub ||
+        op_id == IrBinOpMult ||
+        op_id == IrBinOpDivUnspecified ||
+        op_id == IrBinOpDivTrunc ||
+        op_id == IrBinOpDivFloor ||
+        op_id == IrBinOpDivExact ||
+        op_id == IrBinOpRemRem ||
+        op_id == IrBinOpRemMod))
     {
         // float
     } else {
@@ -8494,6 +8580,18 @@ static TypeTableEntry *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstructionBinOp
         return ira->codegen->builtin_types.entry_invalid;
     }
 
+    if (resolved_type->id == TypeTableEntryIdNumLitInt) {
+        if (op_id == IrBinOpBitShiftLeftWrap) {
+            op_id = IrBinOpBitShiftLeft;
+        } else if (op_id == IrBinOpAddWrap) {
+            op_id = IrBinOpAdd;
+        } else if (op_id == IrBinOpSubWrap) {
+            op_id = IrBinOpSub;
+        } else if (op_id == IrBinOpMultWrap) {
+            op_id = IrBinOpMult;
+        }
+    }
+
     IrInstruction *casted_op1 = ir_implicit_cast(ira, op1, resolved_type);
     if (casted_op1 == ira->codegen->invalid_instruction)
         return ira->codegen->builtin_types.entry_invalid;
@@ -8502,8 +8600,7 @@ static TypeTableEntry *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstructionBinOp
     if (casted_op2 == ira->codegen->invalid_instruction)
         return ira->codegen->builtin_types.entry_invalid;
 
-
-    if (casted_op1->value.special != ConstValSpecialRuntime && casted_op2->value.special != ConstValSpecialRuntime) {
+    if (instr_is_comptime(casted_op1) && instr_is_comptime(casted_op2)) {
         ConstExprValue *op1_val = &casted_op1->value;
         ConstExprValue *op2_val = &casted_op2->value;
         ConstExprValue *out_val = &bin_op_instruction->base.value;
@@ -8704,17 +8801,17 @@ static TypeTableEntry *ir_analyze_array_mult(IrAnalyze *ira, IrInstructionBinOp
     }
 
     uint64_t old_array_len = array_type->data.array.len;
+    uint64_t new_array_len;
 
-    BigNum array_len;
-    bignum_init_unsigned(&array_len, old_array_len);
-    if (bignum_multiply_by_scalar(&array_len, mult_amt)) {
+    if (__builtin_umulll_overflow((unsigned long long)old_array_len, (unsigned long long)mult_amt, 
+                (unsigned long long*)&new_array_len))
+    {
         ir_add_error(ira, &instruction->base, buf_sprintf("operation results in overflow"));
         return ira->codegen->builtin_types.entry_invalid;
     }
 
     ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base);
 
-    uint64_t new_array_len = array_len.data.x_uint;
     out_val->data.x_array.s_none.elements = create_const_vals(new_array_len);
 
     expand_undef_array(ira->codegen, array_val);
@@ -9581,9 +9678,10 @@ static TypeTableEntry *ir_analyze_negation(IrAnalyze *ira, IrInstructionUnOp *un
 
     bool is_wrap_op = (un_op_instruction->op_id == IrUnOpNegationWrap);
 
+    bool is_float = (expr_type->id == TypeTableEntryIdFloat || expr_type->id == TypeTableEntryIdNumLitFloat);
+
     if ((expr_type->id == TypeTableEntryIdInt && expr_type->data.integral.is_signed) ||
-        expr_type->id == TypeTableEntryIdNumLitInt ||
-        ((expr_type->id == TypeTableEntryIdFloat || expr_type->id == TypeTableEntryIdNumLitFloat) && !is_wrap_op))
+        expr_type->id == TypeTableEntryIdNumLitInt || (is_float && !is_wrap_op))
     {
         if (instr_is_comptime(value)) {
             ConstExprValue *target_const_val = ir_resolve_const(ira, value, UndefBad);
@@ -9591,19 +9689,19 @@ static TypeTableEntry *ir_analyze_negation(IrAnalyze *ira, IrInstructionUnOp *un
                 return ira->codegen->builtin_types.entry_invalid;
 
             ConstExprValue *out_val = ir_build_const_from(ira, &un_op_instruction->base);
-            bignum_negate(&out_val->data.x_bignum, &target_const_val->data.x_bignum);
-            if (expr_type->id == TypeTableEntryIdFloat ||
-                expr_type->id == TypeTableEntryIdNumLitFloat ||
-                expr_type->id == TypeTableEntryIdNumLitInt)
-            {
+            if (is_float) {
+                bigfloat_negate(&out_val->data.x_bigfloat, &target_const_val->data.x_bigfloat);
+            } else if (is_wrap_op) {
+                bigint_negate_wrap(&out_val->data.x_bigint, &target_const_val->data.x_bigint,
+                        expr_type->data.integral.bit_count);
+            } else {
+                bigint_negate(&out_val->data.x_bigint, &target_const_val->data.x_bigint);
+            }
+            if (is_wrap_op || is_float || expr_type->id == TypeTableEntryIdNumLitInt) {
                 return expr_type;
             }
 
-            bool overflow = !bignum_fits_in_bits(&out_val->data.x_bignum, expr_type->data.integral.bit_count, true);
-            if (is_wrap_op) {
-                if (overflow)
-                    out_val->data.x_bignum.is_negative = true;
-            } else if (overflow) {
+            if (!bigint_fits_in_bits(&out_val->data.x_bigint, expr_type->data.integral.bit_count, true)) {
                 ir_add_error(ira, &un_op_instruction->base, buf_sprintf("negation caused overflow"));
                 return ira->codegen->builtin_types.entry_invalid;
             }
@@ -9632,7 +9730,7 @@ static TypeTableEntry *ir_analyze_bin_not(IrAnalyze *ira, IrInstructionUnOp *ins
                 return ira->codegen->builtin_types.entry_invalid;
 
             ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base);
-            bignum_not(&out_val->data.x_bignum, &target_const_val->data.x_bignum,
+            bigint_not(&out_val->data.x_bigint, &target_const_val->data.x_bigint,
                     expr_type->data.integral.bit_count, expr_type->data.integral.is_signed);
             return expr_type;
         }
@@ -9887,12 +9985,12 @@ static TypeTableEntry *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstruc
             return_type = get_pointer_to_type_extra(ira->codegen, child_type,
                     ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile, 0, 0);
         } else {
-            ConstExprValue *elem_val = ir_resolve_const(ira, elem_index, UndefBad);
-            if (!elem_val)
+            uint64_t elem_val_scalar;
+            if (!ir_resolve_usize(ira, elem_index, &elem_val_scalar))
                 return ira->codegen->builtin_types.entry_invalid;
 
             size_t bit_width = type_size_bits(ira->codegen, child_type);
-            size_t bit_offset = bit_width * elem_val->data.x_bignum.data.x_uint;
+            size_t bit_offset = bit_width * elem_val_scalar;
 
             return_type = get_pointer_to_type_extra(ira->codegen, child_type,
                     ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile,
@@ -9909,10 +10007,10 @@ static TypeTableEntry *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstruc
         ConstExprValue *args_val = const_ptr_pointee(ira->codegen, ptr_val);
         size_t start = args_val->data.x_arg_tuple.start_index;
         size_t end = args_val->data.x_arg_tuple.end_index;
-        ConstExprValue *elem_index_val = ir_resolve_const(ira, elem_index, UndefBad);
-        if (!elem_index_val)
+        uint64_t elem_index_val;
+        if (!ir_resolve_usize(ira, elem_index, &elem_index_val))
             return ira->codegen->builtin_types.entry_invalid;
-        size_t index = bignum_to_twos_complement(&elem_index_val->data.x_bignum);
+        size_t index = elem_index_val;
         size_t len = end - start;
         if (index >= len) {
             ir_add_error(ira, &elem_ptr_instruction->base,
@@ -9945,7 +10043,7 @@ static TypeTableEntry *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstruc
 
     bool safety_check_on = elem_ptr_instruction->safety_check_on;
     if (instr_is_comptime(casted_elem_index)) {
-        uint64_t index = casted_elem_index->value.data.x_bignum.data.x_uint;
+        uint64_t index = bigint_as_unsigned(&casted_elem_index->value.data.x_bigint);
         if (array_type->id == TypeTableEntryIdArray) {
             uint64_t array_len = array_type->data.array.len;
             if (index >= array_len) {
@@ -10021,7 +10119,7 @@ static TypeTableEntry *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstruc
                 }
                 ConstExprValue *len_field = &array_ptr_val->data.x_struct.fields[slice_len_index];
                 ConstExprValue *out_val = ir_build_const_from(ira, &elem_ptr_instruction->base);
-                uint64_t slice_len = len_field->data.x_bignum.data.x_uint;
+                uint64_t slice_len = bigint_as_unsigned(&len_field->data.x_bigint);
                 if (index >= slice_len) {
                     ir_add_error_node(ira, elem_ptr_instruction->base.source_node,
                         buf_sprintf("index %" ZIG_PRI_u64 " outside slice of size %" ZIG_PRI_u64,
@@ -11107,7 +11205,7 @@ static TypeTableEntry *ir_analyze_instruction_size_of(IrAnalyze *ira,
             {
                 uint64_t size_in_bytes = type_size(ira->codegen, type_entry);
                 ConstExprValue *out_val = ir_build_const_from(ira, &size_of_instruction->base);
-                bignum_init_unsigned(&out_val->data.x_bignum, size_in_bytes);
+                bigint_init_unsigned(&out_val->data.x_bigint, size_in_bytes);
                 return ira->codegen->builtin_types.entry_num_lit_int;
             }
     }
@@ -11213,10 +11311,10 @@ static TypeTableEntry *ir_analyze_instruction_ctz(IrAnalyze *ira, IrInstructionC
         return ira->codegen->builtin_types.entry_invalid;
     } else if (value->value.type->id == TypeTableEntryIdInt) {
         if (value->value.special != ConstValSpecialRuntime) {
-            uint32_t result = bignum_ctz(&value->value.data.x_bignum,
+            size_t result = bigint_ctz(&value->value.data.x_bigint,
                     value->value.type->data.integral.bit_count);
             ConstExprValue *out_val = ir_build_const_from(ira, &ctz_instruction->base);
-            bignum_init_unsigned(&out_val->data.x_bignum, result);
+            bigint_init_unsigned(&out_val->data.x_bigint, result);
             return value->value.type;
         }
 
@@ -11235,10 +11333,10 @@ static TypeTableEntry *ir_analyze_instruction_clz(IrAnalyze *ira, IrInstructionC
         return ira->codegen->builtin_types.entry_invalid;
     } else if (value->value.type->id == TypeTableEntryIdInt) {
         if (value->value.special != ConstValSpecialRuntime) {
-            uint32_t result = bignum_clz(&value->value.data.x_bignum,
+            size_t result = bigint_clz(&value->value.data.x_bigint,
                     value->value.type->data.integral.bit_count);
             ConstExprValue *out_val = ir_build_const_from(ira, &clz_instruction->base);
-            bignum_init_unsigned(&out_val->data.x_bignum, result);
+            bigint_init_unsigned(&out_val->data.x_bigint, result);
             return value->value.type;
         }
 
@@ -11272,7 +11370,7 @@ static IrInstruction *ir_analyze_enum_tag(IrAnalyze *ira, IrInstruction *source_
                 source_instr->scope, source_instr->source_node);
         const_instruction->base.value.type = tag_type;
         const_instruction->base.value.special = ConstValSpecialStatic;
-        bignum_init_unsigned(&const_instruction->base.value.data.x_bignum, val->data.x_enum.tag);
+        bigint_init_unsigned(&const_instruction->base.value.data.x_bigint, val->data.x_enum.tag);
         return &const_instruction->base;
     }
 
@@ -11441,7 +11539,7 @@ static TypeTableEntry *ir_analyze_instruction_switch_target(IrAnalyze *ira,
                 TypeTableEntry *tag_type = target_type->data.enumeration.tag_type;
                 if (pointee_val) {
                     ConstExprValue *out_val = ir_build_const_from(ira, &switch_target_instruction->base);
-                    bignum_init_unsigned(&out_val->data.x_bignum, pointee_val->data.x_enum.tag);
+                    bigint_init_unsigned(&out_val->data.x_bigint, pointee_val->data.x_enum.tag);
                     return tag_type;
                 }
 
@@ -11490,9 +11588,9 @@ static TypeTableEntry *ir_analyze_instruction_switch_var(IrAnalyze *ira, IrInstr
         if (!prong_val)
             return ira->codegen->builtin_types.entry_invalid;
 
-        TypeEnumField *field = &target_type->data.enumeration.fields[prong_val->data.x_bignum.data.x_uint];
+        TypeEnumField *field;
         if (prong_value->value.type->id == TypeTableEntryIdEnumTag) {
-            field = &target_type->data.enumeration.fields[prong_val->data.x_bignum.data.x_uint];
+            field = &target_type->data.enumeration.fields[bigint_as_unsigned(&prong_val->data.x_bigint)];
         } else if (prong_value->value.type->id == TypeTableEntryIdEnum) {
             field = &target_type->data.enumeration.fields[prong_val->data.x_enum.tag];
         } else {
@@ -11619,7 +11717,7 @@ static TypeTableEntry *ir_analyze_instruction_array_len(IrAnalyze *ira,
             ConstExprValue *len_val = &array_value->value.data.x_struct.fields[slice_len_index];
             if (len_val->special != ConstValSpecialRuntime) {
                 return ir_analyze_const_usize(ira, &array_len_instruction->base,
-                        len_val->data.x_bignum.data.x_uint);
+                        bigint_as_unsigned(&len_val->data.x_bigint));
             }
         }
         TypeStructField *field = &type_entry->data.structure.fields[slice_len_index];
@@ -11866,7 +11964,7 @@ static TypeTableEntry *ir_analyze_instruction_container_init_list(IrAnalyze *ira
 
         TypeTableEntry *enum_type = container_type_value->value.type->data.enum_tag.enum_type;
 
-        uint64_t tag_uint = tag_value->data.x_bignum.data.x_uint;
+        uint64_t tag_uint = bigint_as_unsigned(&tag_value->data.x_bigint);
         TypeEnumField *field = &enum_type->data.enumeration.fields[tag_uint];
         TypeTableEntry *this_field_type = field->type_entry;
 
@@ -12063,7 +12161,7 @@ static TypeTableEntry *ir_analyze_instruction_enum_tag_name(IrAnalyze *ira, IrIn
 
     if (instr_is_comptime(target)) {
         TypeTableEntry *enum_type = target->value.type->data.enum_tag.enum_type;
-        uint64_t tag_value = target->value.data.x_bignum.data.x_uint;
+        uint64_t tag_value = bigint_as_unsigned(&target->value.data.x_bigint);
         TypeEnumField *field = &enum_type->data.enumeration.fields[tag_value];
         ConstExprValue *array_val = create_const_str_lit(ira->codegen, field->name);
         ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base);
@@ -12197,7 +12295,7 @@ static TypeTableEntry *ir_analyze_instruction_offset_of(IrAnalyze *ira,
 
     size_t byte_offset = LLVMOffsetOfElement(ira->codegen->target_data_ref, container_type->type_ref, field->gen_index);
     ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base);
-    bignum_init_unsigned(&out_val->data.x_bignum, byte_offset);
+    bigint_init_unsigned(&out_val->data.x_bigint, byte_offset);
     return ira->codegen->builtin_types.entry_num_lit_int;
 }
 
@@ -12506,8 +12604,8 @@ static TypeTableEntry *ir_analyze_instruction_truncate(IrAnalyze *ira, IrInstruc
 
     if (target->value.special == ConstValSpecialStatic) {
         ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base);
-        bignum_init_bignum(&out_val->data.x_bignum, &target->value.data.x_bignum);
-        bignum_truncate(&out_val->data.x_bignum, dest_type->data.integral.bit_count);
+        bigint_truncate(&out_val->data.x_bigint, &target->value.data.x_bigint, dest_type->data.integral.bit_count,
+                dest_type->data.integral.is_signed);
         return dest_type;
     }
 
@@ -12619,7 +12717,7 @@ static TypeTableEntry *ir_analyze_instruction_memset(IrAnalyze *ira, IrInstructi
                 zig_unreachable();
         }
 
-        size_t count = casted_count->value.data.x_bignum.data.x_uint;
+        size_t count = bigint_as_unsigned(&casted_count->value.data.x_bigint);
         size_t end = start + count;
         if (end > bound_end) {
             ir_add_error(ira, count_value, buf_sprintf("out of bounds pointer access"));
@@ -12681,7 +12779,7 @@ static TypeTableEntry *ir_analyze_instruction_memcpy(IrAnalyze *ira, IrInstructi
         casted_count->value.special == ConstValSpecialStatic &&
         casted_dest_ptr->value.data.x_ptr.special != ConstPtrSpecialHardCodedAddr)
     {
-        size_t count = casted_count->value.data.x_bignum.data.x_uint;
+        size_t count = bigint_as_unsigned(&casted_count->value.data.x_bigint);
 
         ConstExprValue *dest_ptr_val = &casted_dest_ptr->value;
         ConstExprValue *dest_elements;
@@ -12868,21 +12966,21 @@ static TypeTableEntry *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstructio
                 case ConstPtrSpecialBaseArray:
                     array_val = parent_ptr->data.x_ptr.data.base_array.array_val;
                     abs_offset = parent_ptr->data.x_ptr.data.base_array.elem_index;
-                    rel_end = len_val->data.x_bignum.data.x_uint;
+                    rel_end = bigint_as_unsigned(&len_val->data.x_bigint);
                     break;
                 case ConstPtrSpecialBaseStruct:
                     zig_panic("TODO slice const inner struct");
                 case ConstPtrSpecialHardCodedAddr:
                     array_val = nullptr;
                     abs_offset = 0;
-                    rel_end = len_val->data.x_bignum.data.x_uint;
+                    rel_end = bigint_as_unsigned(&len_val->data.x_bigint);
                     break;
             }
         } else {
             zig_unreachable();
         }
 
-        uint64_t start_scalar = casted_start->value.data.x_bignum.data.x_uint;
+        uint64_t start_scalar = bigint_as_unsigned(&casted_start->value.data.x_bigint);
         if (start_scalar > rel_end) {
             ir_add_error(ira, &instruction->base, buf_sprintf("out of bounds slice"));
             return ira->codegen->builtin_types.entry_invalid;
@@ -12890,7 +12988,7 @@ static TypeTableEntry *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstructio
 
         uint64_t end_scalar;
         if (end) {
-            end_scalar = end->value.data.x_bignum.data.x_uint;
+            end_scalar = bigint_as_unsigned(&end->value.data.x_bigint);
         } else {
             end_scalar = rel_end;
         }
@@ -12970,7 +13068,7 @@ static TypeTableEntry *ir_analyze_instruction_member_count(IrAnalyze *ira, IrIns
     }
 
     ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base);
-    bignum_init_unsigned(&out_val->data.x_bignum, result);
+    bigint_init_unsigned(&out_val->data.x_bigint, result);
     return ira->codegen->builtin_types.entry_num_lit_int;
 }
 
@@ -13011,7 +13109,7 @@ static TypeTableEntry *ir_analyze_instruction_alignof(IrAnalyze *ira, IrInstruct
     } else {
         uint64_t align_in_bytes = LLVMABIAlignmentOfType(ira->codegen->target_data_ref, type_entry->type_ref);
         ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base);
-        bignum_init_unsigned(&out_val->data.x_bignum, align_in_bytes);
+        bigint_init_unsigned(&out_val->data.x_bigint, align_in_bytes);
         return ira->codegen->builtin_types.entry_num_lit_int;
     }
 }
@@ -13060,29 +13158,32 @@ static TypeTableEntry *ir_analyze_instruction_overflow_op(IrAnalyze *ira, IrInst
         casted_result_ptr->value.special == ConstValSpecialStatic)
     {
         ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base);
-        BigNum *op1_bignum = &casted_op1->value.data.x_bignum;
-        BigNum *op2_bignum = &casted_op2->value.data.x_bignum;
+        BigInt *op1_bigint = &casted_op1->value.data.x_bigint;
+        BigInt *op2_bigint = &casted_op2->value.data.x_bigint;
         ConstExprValue *pointee_val = const_ptr_pointee(ira->codegen, &casted_result_ptr->value);
-        BigNum *dest_bignum = &pointee_val->data.x_bignum;
+        BigInt *dest_bigint = &pointee_val->data.x_bigint;
         switch (instruction->op) {
             case IrOverflowOpAdd:
-                out_val->data.x_bool = bignum_add(dest_bignum, op1_bignum, op2_bignum);
+                bigint_add(dest_bigint, op1_bigint, op2_bigint);
                 break;
             case IrOverflowOpSub:
-                out_val->data.x_bool = bignum_sub(dest_bignum, op1_bignum, op2_bignum);
+                bigint_sub(dest_bigint, op1_bigint, op2_bigint);
                 break;
             case IrOverflowOpMul:
-                out_val->data.x_bool = bignum_mul(dest_bignum, op1_bignum, op2_bignum);
+                bigint_mul(dest_bigint, op1_bigint, op2_bigint);
                 break;
             case IrOverflowOpShl:
-                out_val->data.x_bool = bignum_shl(dest_bignum, op1_bignum, op2_bignum);
+                bigint_shl(dest_bigint, op1_bigint, op2_bigint);
                 break;
         }
-        if (!bignum_fits_in_bits(dest_bignum, dest_type->data.integral.bit_count,
+        if (!bigint_fits_in_bits(dest_bigint, dest_type->data.integral.bit_count,
             dest_type->data.integral.is_signed))
         {
             out_val->data.x_bool = true;
-            bignum_truncate(dest_bignum, dest_type->data.integral.bit_count);
+            BigInt tmp_bigint;
+            bigint_init_bigint(&tmp_bigint, dest_bigint);
+            bigint_truncate(dest_bigint, &tmp_bigint, dest_type->data.integral.bit_count,
+                    dest_type->data.integral.is_signed);
         }
         pointee_val->special = ConstValSpecialStatic;
         return ira->codegen->builtin_types.entry_bool;
@@ -13301,14 +13402,14 @@ static TypeTableEntry *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira
             size_t start_index;
             size_t end_index;
             if (start_value->value.type->id == TypeTableEntryIdEnumTag) {
-                start_index = start_value->value.data.x_bignum.data.x_uint;
+                start_index = bigint_as_unsigned(&start_value->value.data.x_bigint);
             } else if (start_value->value.type->id == TypeTableEntryIdEnum) {
                 start_index = start_value->value.data.x_enum.tag;
             } else {
                 zig_unreachable();
             }
             if (end_value->value.type->id == TypeTableEntryIdEnumTag) {
-                end_index = end_value->value.data.x_bignum.data.x_uint;
+                end_index = bigint_as_unsigned(&end_value->value.data.x_bigint);
             } else if (end_value->value.type->id == TypeTableEntryIdEnum) {
                 end_index = end_value->value.data.x_enum.tag;
             } else {
@@ -13357,7 +13458,7 @@ static TypeTableEntry *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira
             if (!end_val)
                 return ira->codegen->builtin_types.entry_invalid;
 
-            AstNode *prev_node = rangeset_add_range(&rs, &start_val->data.x_bignum, &end_val->data.x_bignum,
+            AstNode *prev_node = rangeset_add_range(&rs, &start_val->data.x_bigint, &end_val->data.x_bigint,
                     start_value->source_node);
             if (prev_node != nullptr) {
                 ErrorMsg *msg = ir_add_error(ira, start_value, buf_sprintf("duplicate switch value"));
@@ -13366,9 +13467,9 @@ static TypeTableEntry *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira
             }
         }
         if (!instruction->have_else_prong) {
-            BigNum min_val;
+            BigInt min_val;
             eval_min_max_value_int(ira->codegen, switch_type, &min_val, false);
-            BigNum max_val;
+            BigInt max_val;
             eval_min_max_value_int(ira->codegen, switch_type, &max_val, true);
             if (!rangeset_spans(&rs, &min_val, &max_val)) {
                 ir_add_error(ira, &instruction->base, buf_sprintf("switch must handle all possibilities"));
@@ -13503,16 +13604,18 @@ static void buf_write_value_bytes(CodeGen *codegen, uint8_t *buf, ConstExprValue
             buf[0] = val->data.x_bool ? 1 : 0;
             return;
         case TypeTableEntryIdInt:
-            bignum_write_twos_complement(&val->data.x_bignum, buf, val->type->data.integral.bit_count, codegen->is_big_endian);
+            bigint_write_twos_complement(&val->data.x_bigint, buf, val->type->data.integral.bit_count,
+                    codegen->is_big_endian);
             return;
         case TypeTableEntryIdFloat:
-            bignum_write_ieee597(&val->data.x_bignum, buf, val->type->data.floating.bit_count, codegen->is_big_endian);
+            bigfloat_write_ieee597(&val->data.x_bigfloat, buf, val->type->data.floating.bit_count,
+                    codegen->is_big_endian);
             return;
         case TypeTableEntryIdPointer:
             if (val->data.x_ptr.special == ConstPtrSpecialHardCodedAddr) {
-                BigNum bn;
-                bignum_init_unsigned(&bn, val->data.x_ptr.data.hard_coded_addr.addr);
-                bignum_write_twos_complement(&bn, buf, codegen->builtin_types.entry_usize->data.integral.bit_count, codegen->is_big_endian);
+                BigInt bn;
+                bigint_init_unsigned(&bn, val->data.x_ptr.data.hard_coded_addr.addr);
+                bigint_write_twos_complement(&bn, buf, codegen->builtin_types.entry_usize->data.integral.bit_count, codegen->is_big_endian);
                 return;
             } else {
                 zig_unreachable();
@@ -13562,18 +13665,20 @@ static void buf_read_value_bytes(CodeGen *codegen, uint8_t *buf, ConstExprValue
             val->data.x_bool = (buf[0] != 0);
             return;
         case TypeTableEntryIdInt:
-            bignum_read_twos_complement(&val->data.x_bignum, buf, val->type->data.integral.bit_count, codegen->is_big_endian,
-                    val->type->data.integral.is_signed);
+            bigint_read_twos_complement(&val->data.x_bigint, buf, val->type->data.integral.bit_count,
+                    codegen->is_big_endian, val->type->data.integral.is_signed);
             return;
         case TypeTableEntryIdFloat:
-            bignum_read_ieee597(&val->data.x_bignum, buf, val->type->data.floating.bit_count, codegen->is_big_endian);
+            bigfloat_read_ieee597(&val->data.x_bigfloat, buf, val->type->data.floating.bit_count,
+                    codegen->is_big_endian);
             return;
         case TypeTableEntryIdPointer:
             {
                 val->data.x_ptr.special = ConstPtrSpecialHardCodedAddr;
-                BigNum bn;
-                bignum_read_twos_complement(&bn, buf, codegen->builtin_types.entry_usize->data.integral.bit_count, codegen->is_big_endian, false);
-                val->data.x_ptr.data.hard_coded_addr.addr = bignum_to_twos_complement(&bn);
+                BigInt bn;
+                bigint_read_twos_complement(&bn, buf, codegen->builtin_types.entry_usize->data.integral.bit_count,
+                        codegen->is_big_endian, false);
+                val->data.x_ptr.data.hard_coded_addr.addr = bigint_as_unsigned(&bn);
                 return;
             }
         case TypeTableEntryIdArray:
@@ -13729,7 +13834,7 @@ static TypeTableEntry *ir_analyze_instruction_int_to_ptr(IrAnalyze *ira, IrInstr
 
         ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base);
         out_val->data.x_ptr.special = ConstPtrSpecialHardCodedAddr;
-        out_val->data.x_ptr.data.hard_coded_addr.addr = bignum_to_twos_complement(&val->data.x_bignum);
+        out_val->data.x_ptr.data.hard_coded_addr.addr = bigint_as_unsigned(&val->data.x_bigint);
         return dest_type;
     }
 
src/os.hpp
@@ -13,6 +13,7 @@
 #include "error.hpp"
 
 #include <stdio.h>
+#include <inttypes.h>
 
 enum TerminationId {
     TerminationIdClean,
src/parser.cpp
@@ -186,9 +186,14 @@ static Buf *token_buf(Token *token) {
     return &token->data.str_lit.str;
 }
 
-static BigNum *token_bignum(Token *token) {
-    assert(token->id == TokenIdNumberLiteral);
-    return &token->data.num_lit.bignum;
+static BigInt *token_bigint(Token *token) {
+    assert(token->id == TokenIdIntLiteral);
+    return &token->data.int_lit.bigint;
+}
+
+static BigFloat *token_bigfloat(Token *token) {
+    assert(token->id == TokenIdFloatLiteral);
+    return &token->data.float_lit.bigfloat;
 }
 
 static uint8_t token_char_lit(Token *token) {
@@ -660,16 +665,21 @@ static AstNode *ast_parse_comptime_expr(ParseContext *pc, size_t *token_index, b
 }
 
 /*
-PrimaryExpression = Number | String | CharLiteral | KeywordLiteral | GroupedExpression | GotoExpression | BlockExpression(BlockOrExpression) | Symbol | ("@" Symbol FnCallExpression) | ArrayType | (option("extern") FnProto) | AsmExpression | ("error" "." Symbol) | ContainerDecl
+PrimaryExpression = Integer | Float | String | CharLiteral | KeywordLiteral | GroupedExpression | GotoExpression | BlockExpression(BlockOrExpression) | Symbol | ("@" Symbol FnCallExpression) | ArrayType | (option("extern") FnProto) | AsmExpression | ("error" "." Symbol) | ContainerDecl
 KeywordLiteral = "true" | "false" | "null" | "continue" | "undefined" | "error" | "this" | "unreachable"
 */
 static AstNode *ast_parse_primary_expr(ParseContext *pc, size_t *token_index, bool mandatory) {
     Token *token = &pc->tokens->at(*token_index);
 
-    if (token->id == TokenIdNumberLiteral) {
-        AstNode *node = ast_create_node(pc, NodeTypeNumberLiteral, token);
-        node->data.number_literal.bignum = token_bignum(token);
-        node->data.number_literal.overflow = token->data.num_lit.overflow;
+    if (token->id == TokenIdIntLiteral) {
+        AstNode *node = ast_create_node(pc, NodeTypeIntLiteral, token);
+        node->data.int_literal.bigint = token_bigint(token);
+        *token_index += 1;
+        return node;
+    } else if (token->id == TokenIdFloatLiteral) {
+        AstNode *node = ast_create_node(pc, NodeTypeFloatLiteral, token);
+        node->data.float_literal.bigfloat = token_bigfloat(token);
+        node->data.float_literal.overflow = token->data.float_lit.overflow;
         *token_index += 1;
         return node;
     } else if (token->id == TokenIdStringLiteral) {
@@ -2629,7 +2639,10 @@ void ast_visit_node_children(AstNode *node, void (*visit)(AstNode **, void *cont
             visit_field(&node->data.unwrap_err_expr.symbol, visit, context);
             visit_field(&node->data.unwrap_err_expr.op2, visit, context);
             break;
-        case NodeTypeNumberLiteral:
+        case NodeTypeIntLiteral:
+            // none
+            break;
+        case NodeTypeFloatLiteral:
             // none
             break;
         case NodeTypeStringLiteral:
src/range_set.cpp
@@ -1,11 +1,11 @@
 #include "range_set.hpp"
 
-AstNode *rangeset_add_range(RangeSet *rs, BigNum *first, BigNum *last, AstNode *source_node) {
+AstNode *rangeset_add_range(RangeSet *rs, BigInt *first, BigInt *last, AstNode *source_node) {
     for (size_t i = 0; i < rs->src_range_list.length; i += 1) {
         RangeWithSrc *range_with_src = &rs->src_range_list.at(i);
         Range *range = &range_with_src->range;
-        if ((bignum_cmp_gte(first, &range->first) && bignum_cmp_lte(first, &range->last)) ||
-            (bignum_cmp_gte(last, &range->first) && bignum_cmp_lte(last, &range->last)))
+        if ((bigint_cmp(first, &range->first) != CmpLT && bigint_cmp(first, &range->last) != CmpGT) ||
+            (bigint_cmp(last, &range->first) != CmpLT && bigint_cmp(last, &range->last) != CmpGT))
         {
             return range_with_src->source_node;
         }
@@ -16,24 +16,22 @@ AstNode *rangeset_add_range(RangeSet *rs, BigNum *first, BigNum *last, AstNode *
 
 }
 
-static bool add_range(ZigList<Range> *list, Range *new_range, BigNum *one) {
+static bool add_range(ZigList<Range> *list, Range *new_range, BigInt *one) {
     for (size_t i = 0; i < list->length; i += 1) {
         Range *range = &list->at(i);
 
-        BigNum first_minus_one;
-        if (bignum_sub(&first_minus_one, &range->first, one))
-            zig_unreachable();
+        BigInt first_minus_one;
+        bigint_sub(&first_minus_one, &range->first, one);
 
-        if (bignum_cmp_eq(&new_range->last, &first_minus_one)) {
+        if (bigint_cmp(&new_range->last, &first_minus_one) == CmpEQ) {
             range->first = new_range->first;
             return true;
         }
 
-        BigNum last_plus_one;
-        if (bignum_add(&last_plus_one, &range->last, one))
-            zig_unreachable();
+        BigInt last_plus_one;
+        bigint_add(&last_plus_one, &range->last, one);
 
-        if (bignum_cmp_eq(&new_range->first, &last_plus_one)) {
+        if (bigint_cmp(&new_range->first, &last_plus_one) == CmpEQ) {
             range->last = new_range->last;
             return true;
         }
@@ -42,7 +40,7 @@ static bool add_range(ZigList<Range> *list, Range *new_range, BigNum *one) {
     return false;
 }
 
-bool rangeset_spans(RangeSet *rs, BigNum *first, BigNum *last) {
+bool rangeset_spans(RangeSet *rs, BigInt *first, BigInt *last) {
     ZigList<Range> cur_list_value = {0};
     ZigList<Range> other_list_value = {0};
     ZigList<Range> *cur_list = &cur_list_value;
@@ -54,8 +52,8 @@ bool rangeset_spans(RangeSet *rs, BigNum *first, BigNum *last) {
         cur_list->append({range->first, range->last});
     }
 
-    BigNum one;
-    bignum_init_unsigned(&one, 1);
+    BigInt one;
+    bigint_init_unsigned(&one, 1);
 
     bool changes_made = true;
     while (changes_made) {
@@ -73,9 +71,9 @@ bool rangeset_spans(RangeSet *rs, BigNum *first, BigNum *last) {
     if (cur_list->length != 1)
         return false;
     Range *range = &cur_list->at(0);
-    if (bignum_cmp_neq(&range->first, first))
+    if (bigint_cmp(&range->first, first) != CmpEQ)
         return false;
-    if (bignum_cmp_neq(&range->last, last))
+    if (bigint_cmp(&range->last, last) != CmpEQ)
         return false;
     return true;
 }
src/range_set.hpp
@@ -11,8 +11,8 @@
 #include "all_types.hpp"
 
 struct Range {
-    BigNum first;
-    BigNum last;
+    BigInt first;
+    BigInt last;
 };
 
 struct RangeWithSrc {
@@ -24,7 +24,7 @@ struct RangeSet {
     ZigList<RangeWithSrc> src_range_list;
 };
 
-AstNode *rangeset_add_range(RangeSet *rs, BigNum *first, BigNum *last, AstNode *source_node);
-bool rangeset_spans(RangeSet *rs, BigNum *first, BigNum *last);
+AstNode *rangeset_add_range(RangeSet *rs, BigInt *first, BigInt *last, AstNode *source_node);
+bool rangeset_spans(RangeSet *rs, BigInt *first, BigInt *last);
 
 #endif
src/tokenizer.cpp
@@ -225,13 +225,13 @@ struct Tokenize {
     uint32_t radix;
     int32_t exp_add_amt;
     bool is_exp_negative;
-    bool is_num_lit_float;
     size_t char_code_index;
     size_t char_code_end;
     bool unicode;
     uint32_t char_code;
     int exponent_in_bin_or_dec;
-    BigNum specified_exponent;
+    BigInt specified_exponent;
+    BigInt significand;
 };
 
 __attribute__ ((format (printf, 2, 3)))
@@ -255,8 +255,11 @@ static void tokenize_error(Tokenize *t, const char *format, ...) {
 static void set_token_id(Tokenize *t, Token *token, TokenId id) {
     token->id = id;
 
-    if (id == TokenIdNumberLiteral) {
-        token->data.num_lit.overflow = false;
+    if (id == TokenIdIntLiteral) {
+        bigint_init_unsigned(&token->data.int_lit.bigint, 0);
+    } else if (id == TokenIdFloatLiteral) {
+        bigfloat_init_float(&token->data.float_lit.bigfloat, 0.0);
+        token->data.float_lit.overflow = false;
     } else if (id == TokenIdStringLiteral || id == TokenIdSymbol) {
         memset(&token->data.str_lit.str, 0, sizeof(Buf));
         buf_resize(&token->data.str_lit.str, 0);
@@ -283,34 +286,40 @@ static void cancel_token(Tokenize *t) {
 }
 
 static void end_float_token(Tokenize *t) {
-    t->cur_tok->data.num_lit.bignum.kind = BigNumKindFloat;
-
     if (t->radix == 10) {
-        char *str_begin = buf_ptr(t->buf) + t->cur_tok->start_pos;
-        char *str_end;
-        errno = 0;
-        t->cur_tok->data.num_lit.bignum.data.x_float = strtod(str_begin, &str_end);
-        if (errno) {
-            t->cur_tok->data.num_lit.overflow = true;
-            return;
+        uint8_t *ptr_buf = (uint8_t*)buf_ptr(t->buf) + t->cur_tok->start_pos;
+        size_t buf_len = t->cur_tok->end_pos - t->cur_tok->start_pos;
+        if (bigfloat_init_buf_base10(&t->cur_tok->data.float_lit.bigfloat, ptr_buf, buf_len)) {
+            t->cur_tok->data.float_lit.overflow = true;
         }
-        assert(str_end <= buf_ptr(t->buf) + t->cur_tok->end_pos);
         return;
     }
 
+    BigInt int_max;
+    bigint_init_unsigned(&int_max, INT_MAX);
+
+    if (bigint_cmp(&t->specified_exponent, &int_max) != CmpLT) {
+        t->cur_tok->data.float_lit.overflow = true;
+        return;
+    }
 
-    if (t->specified_exponent.data.x_uint >= INT_MAX) {
-        t->cur_tok->data.num_lit.overflow = true;
+    if (!bigint_fits_in_bits(&t->specified_exponent, 64, true)) {
+        t->cur_tok->data.float_lit.overflow = true;
         return;
     }
 
-    int64_t specified_exponent = t->specified_exponent.data.x_uint;
+    int64_t specified_exponent = bigint_as_signed(&t->specified_exponent);
     if (t->is_exp_negative) {
         specified_exponent = -specified_exponent;
     }
     t->exponent_in_bin_or_dec = (int)(t->exponent_in_bin_or_dec + specified_exponent);
 
-    uint64_t significand = t->cur_tok->data.num_lit.bignum.data.x_uint;
+    if (!bigint_fits_in_bits(&t->significand, 64, false)) {
+        t->cur_tok->data.float_lit.overflow = true;
+        return;
+    }
+
+    uint64_t significand = bigint_as_unsigned(&t->significand);
     uint64_t significand_bits;
     uint64_t exponent_bits;
     if (significand == 0) {
@@ -325,7 +334,7 @@ static void end_float_token(Tokenize *t) {
             int significand_magnitude_in_bin = __builtin_clzll(1) - __builtin_clzll(significand);
             t->exponent_in_bin_or_dec += significand_magnitude_in_bin;
             if (!(-1023 <= t->exponent_in_bin_or_dec && t->exponent_in_bin_or_dec < 1023)) {
-                t->cur_tok->data.num_lit.overflow = true;
+                t->cur_tok->data.float_lit.overflow = true;
                 return;
             } else {
                 // this should chop off exactly one 1 bit from the top.
@@ -335,20 +344,17 @@ static void end_float_token(Tokenize *t) {
         }
     }
     uint64_t double_bits = (exponent_bits << 52) | significand_bits;
-    safe_memcpy(&t->cur_tok->data.num_lit.bignum.data.x_float, (double *)&double_bits, 1);
+    double dbl_value;
+    safe_memcpy(&dbl_value, (double *)&double_bits, 1);
+    bigfloat_init_float(&t->cur_tok->data.float_lit.bigfloat, dbl_value);
 }
 
 static void end_token(Tokenize *t) {
     assert(t->cur_tok);
     t->cur_tok->end_pos = t->pos + 1;
 
-    if (t->cur_tok->id == TokenIdNumberLiteral) {
-        if (t->cur_tok->data.num_lit.overflow) {
-            return;
-        }
-        if (t->is_num_lit_float) {
-            end_float_token(t);
-        }
+    if (t->cur_tok->id == TokenIdFloatLiteral) {
+        end_float_token(t);
     } else if (t->cur_tok->id == TokenIdSymbol) {
         char *token_mem = buf_ptr(t->buf) + t->cur_tok->start_pos;
         int token_len = (int)(t->cur_tok->end_pos - t->cur_tok->start_pos);
@@ -428,23 +434,21 @@ void tokenize(Buf *buf, Tokenization *out) {
                         break;
                     case '0':
                         t.state = TokenizeStateZero;
-                        begin_token(&t, TokenIdNumberLiteral);
+                        begin_token(&t, TokenIdIntLiteral);
                         t.radix = 10;
                         t.exp_add_amt = 1;
                         t.exponent_in_bin_or_dec = 0;
-                        t.is_num_lit_float = false;
-                        bignum_init_unsigned(&t.cur_tok->data.num_lit.bignum, 0);
-                        bignum_init_unsigned(&t.specified_exponent, 0);
+                        bigint_init_unsigned(&t.cur_tok->data.int_lit.bigint, 0);
+                        bigint_init_unsigned(&t.specified_exponent, 0);
                         break;
                     case DIGIT_NON_ZERO:
                         t.state = TokenizeStateNumber;
-                        begin_token(&t, TokenIdNumberLiteral);
+                        begin_token(&t, TokenIdIntLiteral);
                         t.radix = 10;
                         t.exp_add_amt = 1;
                         t.exponent_in_bin_or_dec = 0;
-                        t.is_num_lit_float = false;
-                        bignum_init_unsigned(&t.cur_tok->data.num_lit.bignum, get_digit_value(c));
-                        bignum_init_unsigned(&t.specified_exponent, 0);
+                        bigint_init_unsigned(&t.cur_tok->data.int_lit.bigint, get_digit_value(c));
+                        bigint_init_unsigned(&t.specified_exponent, 0);
                         break;
                     case '"':
                         begin_token(&t, TokenIdStringLiteral);
@@ -1182,7 +1186,9 @@ void tokenize(Buf *buf, Tokenization *out) {
                     }
                     if (is_exponent_signifier(c, t.radix)) {
                         t.state = TokenizeStateFloatExponentUnsigned;
-                        t.is_num_lit_float = true;
+                        assert(t.cur_tok->id == TokenIdIntLiteral);
+                        bigint_init_bigint(&t.significand, &t.cur_tok->data.int_lit.bigint);
+                        set_token_id(&t, t.cur_tok, TokenIdFloatLiteral);
                         break;
                     }
                     uint32_t digit_value = get_digit_value(c);
@@ -1196,23 +1202,33 @@ void tokenize(Buf *buf, Tokenization *out) {
                         t.state = TokenizeStateStart;
                         continue;
                     }
-                    t.cur_tok->data.num_lit.overflow = t.cur_tok->data.num_lit.overflow ||
-                        bignum_multiply_by_scalar(&t.cur_tok->data.num_lit.bignum, t.radix);
-                    t.cur_tok->data.num_lit.overflow = t.cur_tok->data.num_lit.overflow ||
-                        bignum_increment_by_scalar(&t.cur_tok->data.num_lit.bignum, digit_value);
+                    BigInt digit_value_bi;
+                    bigint_init_unsigned(&digit_value_bi, digit_value);
+
+                    BigInt radix_bi;
+                    bigint_init_unsigned(&radix_bi, t.radix);
+
+                    BigInt multiplied;
+                    bigint_mul(&multiplied, &t.cur_tok->data.int_lit.bigint, &radix_bi);
+
+                    bigint_add(&t.cur_tok->data.int_lit.bigint, &multiplied, &digit_value_bi);
                     break;
                 }
             case TokenizeStateNumberDot:
-                if (c == '.') {
-                    t.pos -= 2;
-                    end_token(&t);
-                    t.state = TokenizeStateStart;
+                {
+                    if (c == '.') {
+                        t.pos -= 2;
+                        end_token(&t);
+                        t.state = TokenizeStateStart;
+                        continue;
+                    }
+                    t.pos -= 1;
+                    t.state = TokenizeStateFloatFraction;
+                    assert(t.cur_tok->id == TokenIdIntLiteral);
+                    bigint_init_bigint(&t.significand, &t.cur_tok->data.int_lit.bigint);
+                    set_token_id(&t, t.cur_tok, TokenIdFloatLiteral);
                     continue;
                 }
-                t.pos -= 1;
-                t.state = TokenizeStateFloatFraction;
-                t.is_num_lit_float = true;
-                continue;
             case TokenizeStateFloatFraction:
                 {
                     if (is_exponent_signifier(c, t.radix)) {
@@ -1236,10 +1252,16 @@ void tokenize(Buf *buf, Tokenization *out) {
                         // end of the token.
                         break;
                     }
-                    t.cur_tok->data.num_lit.overflow = t.cur_tok->data.num_lit.overflow ||
-                        bignum_multiply_by_scalar(&t.cur_tok->data.num_lit.bignum, t.radix);
-                    t.cur_tok->data.num_lit.overflow = t.cur_tok->data.num_lit.overflow ||
-                        bignum_increment_by_scalar(&t.cur_tok->data.num_lit.bignum, digit_value);
+                    BigInt digit_value_bi;
+                    bigint_init_unsigned(&digit_value_bi, digit_value);
+
+                    BigInt radix_bi;
+                    bigint_init_unsigned(&radix_bi, t.radix);
+
+                    BigInt multiplied;
+                    bigint_mul(&multiplied, &t.significand, &radix_bi);
+
+                    bigint_add(&t.significand, &multiplied, &digit_value_bi);
                     break;
                 }
             case TokenizeStateFloatExponentUnsigned:
@@ -1278,10 +1300,16 @@ void tokenize(Buf *buf, Tokenization *out) {
                         // end of the token.
                         break;
                     }
-                    t.cur_tok->data.num_lit.overflow = t.cur_tok->data.num_lit.overflow ||
-                        bignum_multiply_by_scalar(&t.specified_exponent, 10);
-                    t.cur_tok->data.num_lit.overflow = t.cur_tok->data.num_lit.overflow ||
-                        bignum_increment_by_scalar(&t.specified_exponent, digit_value);
+                    BigInt digit_value_bi;
+                    bigint_init_unsigned(&digit_value_bi, digit_value);
+
+                    BigInt radix_bi;
+                    bigint_init_unsigned(&radix_bi, 10);
+
+                    BigInt multiplied;
+                    bigint_mul(&multiplied, &t.specified_exponent, &radix_bi);
+
+                    bigint_add(&t.specified_exponent, &multiplied, &digit_value_bi);
                 }
                 break;
             case TokenizeStateSawDash:
@@ -1441,11 +1469,13 @@ const char * token_name(TokenId id) {
         case TokenIdDivEq: return "/=";
         case TokenIdDot: return ".";
         case TokenIdDoubleQuestion: return "??";
-        case TokenIdEllipsis3: return "...";
         case TokenIdEllipsis2: return "..";
+        case TokenIdEllipsis3: return "...";
         case TokenIdEof: return "EOF";
         case TokenIdEq: return "=";
         case TokenIdFatArrow: return "=>";
+        case TokenIdFloatLiteral: return "FloatLiteral";
+        case TokenIdIntLiteral: return "IntLiteral";
         case TokenIdKeywordAnd: return "and";
         case TokenIdKeywordAsm: return "asm";
         case TokenIdKeywordBreak: return "break";
@@ -1494,7 +1524,6 @@ const char * token_name(TokenId id) {
         case TokenIdMinusPercent: return "-%";
         case TokenIdMinusPercentEq: return "-%=";
         case TokenIdModEq: return "%=";
-        case TokenIdNumberLiteral: return "NumberLiteral";
         case TokenIdNumberSign: return "#";
         case TokenIdPercent: return "%";
         case TokenIdPercentDot: return "%.";
src/tokenizer.hpp
@@ -9,7 +9,8 @@
 #define ZIG_TOKENIZER_HPP
 
 #include "buffer.hpp"
-#include "bignum.hpp"
+#include "bigint.hpp"
+#include "bigfloat.hpp"
 
 enum TokenId {
     TokenIdAmpersand,
@@ -40,11 +41,13 @@ enum TokenId {
     TokenIdDivEq,
     TokenIdDot,
     TokenIdDoubleQuestion,
-    TokenIdEllipsis3,
     TokenIdEllipsis2,
+    TokenIdEllipsis3,
     TokenIdEof,
     TokenIdEq,
     TokenIdFatArrow,
+    TokenIdFloatLiteral,
+    TokenIdIntLiteral,
     TokenIdKeywordAnd,
     TokenIdKeywordAsm,
     TokenIdKeywordBreak,
@@ -93,7 +96,6 @@ enum TokenId {
     TokenIdMinusPercent,
     TokenIdMinusPercentEq,
     TokenIdModEq,
-    TokenIdNumberLiteral,
     TokenIdNumberSign,
     TokenIdPercent,
     TokenIdPercentDot,
@@ -118,13 +120,17 @@ enum TokenId {
     TokenIdTimesPercentEq,
 };
 
-struct TokenNumLit {
-    BigNum bignum;
-    // overflow is true if when parsing the number, we discovered it would not
-    // fit without losing data in a uint64_t or double
+struct TokenFloatLit {
+    BigFloat bigfloat;
+    // overflow is true if when parsing the number, we discovered it would not fit
+    // without losing data
     bool overflow;
 };
 
+struct TokenIntLit {
+    BigInt bigint;
+};
+
 struct TokenStrLit {
     Buf str;
     bool is_c_str;
@@ -142,8 +148,11 @@ struct Token {
     size_t start_column;
 
     union {
-        // TokenIdNumberLiteral
-        TokenNumLit num_lit;
+        // TokenIdIntLiteral
+        TokenIntLit int_lit;
+
+        // TokenIdFloatLiteral
+        TokenFloatLit float_lit;
 
         // TokenIdStringLiteral or TokenIdSymbol
         TokenStrLit str_lit;
std/math/fabs.zig
@@ -36,8 +36,8 @@ test "math.fabs" {
 }
 
 test "math.fabs32" {
-    assert(fabs64(1.0) == 1.0);
-    assert(fabs64(-1.0) == 1.0);
+    assert(fabs32(1.0) == 1.0);
+    assert(fabs32(-1.0) == 1.0);
 }
 
 test "math.fabs64" {
std/math/log10.zig
@@ -139,7 +139,7 @@ fn log10_64(x_: f64) -> f64 {
     // hi + lo = f - hfsq + s * (hfsq + R) ~ log(1 + f)
     var hi = f - hfsq;
     var hii = @bitCast(u64, hi);
-    hii &= @maxValue(u64) << 32;
+    hii &= u64(@maxValue(u64)) <<% 32;
     hi = @bitCast(f64, hii);
     const lo = f - hi - hfsq + s * (hfsq + R);
 
std/math/log2.zig
@@ -133,7 +133,7 @@ fn log2_64(x_: f64) -> f64 {
     // hi + lo = f - hfsq + s * (hfsq + R) ~ log(1 + f)
     var hi = f - hfsq;
     var hii = @bitCast(u64, hi);
-    hii &= @maxValue(u64) << 32;
+    hii &= u64(@maxValue(u64)) <<% 32;
     hi = @bitCast(f64, hii);
     const lo = f - hi - hfsq + s * (hfsq + R);
 
test/cases/math.zig
@@ -58,15 +58,33 @@ test "@shlWithOverflow" {
 }
 
 test "@clz" {
-    assert(@clz(u8(0b00001010)) == 4);
-    assert(@clz(u8(0b10001010)) == 0);
-    assert(@clz(u8(0b00000000)) == 8);
+    testClz();
+    comptime testClz();
+}
+
+fn testClz() {
+    assert(clz(u8(0b00001010)) == 4);
+    assert(clz(u8(0b10001010)) == 0);
+    assert(clz(u8(0b00000000)) == 8);
+}
+
+fn clz(x: var) -> usize {
+    @clz(x)
 }
 
 test "@ctz" {
-    assert(@ctz(u8(0b10100000)) == 5);
-    assert(@ctz(u8(0b10001010)) == 1);
-    assert(@ctz(u8(0b00000000)) == 8);
+    testCtz();
+    comptime testCtz();
+}
+
+fn testCtz() {
+    assert(ctz(u8(0b10100000)) == 5);
+    assert(ctz(u8(0b10001010)) == 1);
+    assert(ctz(u8(0b00000000)) == 8);
+}
+
+fn ctz(x: var) -> usize {
+    @ctz(x)
 }
 
 test "assignment operators" {
@@ -229,3 +247,7 @@ test "allow signed integer division/remainder when values are comptime known and
     assert(5 % 3 == 2);
     assert(-6 % 3 == 0);
 }
+
+test "float literal parsing" {
+    comptime assert(0x1.0 == 1.0);
+}
.gitignore
@@ -7,3 +7,4 @@ build-llvm-debug/
 /.cproject
 /.project
 /.settings/
+build-llvm-debug/
CMakeLists.txt
@@ -44,7 +44,8 @@ include_directories(
 set(ZIG_SOURCES
     "${CMAKE_SOURCE_DIR}/src/analyze.cpp"
     "${CMAKE_SOURCE_DIR}/src/ast_render.cpp"
-    "${CMAKE_SOURCE_DIR}/src/bignum.cpp"
+    "${CMAKE_SOURCE_DIR}/src/bigfloat.cpp"
+    "${CMAKE_SOURCE_DIR}/src/bigint.cpp"
     "${CMAKE_SOURCE_DIR}/src/buffer.cpp"
     "${CMAKE_SOURCE_DIR}/src/c_tokenizer.cpp"
     "${CMAKE_SOURCE_DIR}/src/codegen.cpp"