WORKING FIXED POINT

This commit is contained in:
2024-10-31 23:21:09 -04:00
parent 5513ddf25b
commit 88e25da973
14 changed files with 670 additions and 192 deletions

View File

@@ -3,10 +3,11 @@ use std::ops::AddAssign;
use crate::util::FixedDec; use crate::util::FixedDec;
#[derive(Clone, Copy)] #[derive(Clone)]
pub struct Zoom { pub struct Zoom {
scale: f32, exp: f32,
mult: f32, level: i32,
mult: FixedDec,
} }
#[derive(Clone)] #[derive(Clone)]
@@ -30,30 +31,43 @@ impl Default for Camera {
fn default() -> Self { fn default() -> Self {
Self { Self {
pos: Vector2::zeros(), pos: Vector2::zeros(),
zoom: Zoom::new(0.0), zoom: Zoom::new(0, 0.0),
} }
} }
} }
impl Zoom { impl Zoom {
pub fn new(scale: f32) -> Self { pub fn new(level: i32, scale: f32) -> Self {
Self { Self {
scale, exp: scale,
mult: mult(scale), level,
mult: mult(level, scale),
} }
} }
pub fn mult(&self) -> f32 { pub fn mult(&self) -> &FixedDec {
self.mult &self.mult
}
pub fn level(&self) -> i32 {
self.level
} }
} }
impl AddAssign<f32> for Zoom { impl AddAssign<f32> for Zoom {
#[allow(clippy::suspicious_op_assign_impl)]
fn add_assign(&mut self, rhs: f32) { fn add_assign(&mut self, rhs: f32) {
self.scale += rhs; self.exp -= rhs;
self.mult = mult(self.scale); while self.exp <= -0.5 {
self.exp += 1.0;
self.level += 1;
}
while self.exp > 0.5 {
self.exp -= 1.0;
self.level -= 1;
}
self.mult = mult(self.level, self.exp);
} }
} }
pub fn mult(scale: f32) -> f32 { pub fn mult(level: i32, exp: f32) -> FixedDec {
(-scale).exp2() (FixedDec::from(1) >> level) * FixedDec::from(exp.exp2())
} }

View File

@@ -12,10 +12,11 @@ impl Client<'_> {
let per_sec = delta.as_secs_f32(); let per_sec = delta.as_secs_f32();
if input.scroll_delta != 0.0 { if input.scroll_delta != 0.0 {
// camera.zoom += input.scroll_delta / 5.0; camera.zoom += input.scroll_delta / 5.0;
} }
let speed = FixedDec::from(per_sec * 0.5); let speed = FixedDec::from(per_sec * 0.5) * camera.zoom.mult();
let old = f32::from(&camera.pos.x);
if input.pressed(K::KeyW) { if input.pressed(K::KeyW) {
camera.pos.y += &speed; camera.pos.y += &speed;
} }
@@ -28,5 +29,9 @@ impl Client<'_> {
if input.pressed(K::KeyD) { if input.pressed(K::KeyD) {
camera.pos.x += &speed; camera.pos.x += &speed;
} }
let new = f32::from(&camera.pos.x);
if (new - old).abs() > 0.5 {
println!("{} + {} = {}", old, f32::from(speed), new);
}
} }
} }

View File

@@ -0,0 +1,35 @@
@fragment
fn fs_main(
in: VertexOutput,
) -> @location(0) vec4<f32> {
let dec = i32(1) << 13;
let c = vec2<i32>(in.world_pos * f32(dec));
let cx = c.x;
let cy = c.y;
var x = 0;
var y = 0;
var i = 0u;
let thresh = 2 * dec;
let thresh2 = thresh * thresh;
let max = 50u;
loop {
let x2 = x * x;
let y2 = y * y;
if x2 + y2 > thresh2 || i >= max {
break;
}
y = (2 * x * y) / dec + c.y;
x = (x2 - y2) / dec + c.x;
i += 1u;
}
var color = vec3<f32>(0.0, 0.0, 0.0);
if i != max {
let pi = 3.1415;
let hue = f32(i) / 30.0;
color.r = cos(hue);
color.g = cos(hue - 2.0 * pi / 3.0);
color.b = cos(hue - 4.0 * pi / 3.0);
}
return vec4(color, 1.0);
}

View File

@@ -1,30 +1,61 @@
use nalgebra::Vector2; use nalgebra::Vector2;
#[repr(C, align(8))] use crate::util::FixedDec;
#[derive(Clone, Copy)]
use super::Camera;
const VIEW_ALIGN: usize = 4 * 2;
pub struct View { pub struct View {
pub scale: Vector2<f32>, pub bytes: Vec<u8>,
}
impl View {
pub fn bytes(&self) -> &[u8] {
&self.bytes
}
} }
impl Default for View { impl Default for View {
fn default() -> Self { fn default() -> Self {
Self { let val = FixedDec::from_parts(false, 0, vec![0, 0, 0]);
scale: Vector2::zeros(), Self::new(Vector2::zeros(), 0, &val, &val, &val)
}
} }
} }
impl View { impl View {
pub fn new(size: &Vector2<u32>) -> Self { fn new(stretch: Vector2<f32>, level: i32, scale: &FixedDec, x: &FixedDec, y: &FixedDec) -> Self {
let mut bytes = Vec::new();
bytes.extend(bytemuck::cast_slice(&[stretch.x, stretch.y]));
bytes.extend(level.to_le_bytes());
scale.to_bytes(&mut bytes);
x.to_bytes(&mut bytes);
y.to_bytes(&mut bytes);
let rem = bytes.len() % VIEW_ALIGN;
if rem != 0 {
bytes.extend((0..(VIEW_ALIGN - rem)).map(|_| 0));
}
Self{ bytes }
}
pub fn from_camera_size(camera: &Camera, size: &Vector2<u32>) -> Self {
let mut x = camera.pos.x.clone();
x.set_whole_len(1);
x.set_dec_len(2);
let mut y = camera.pos.y.clone();
y.set_whole_len(1);
y.set_dec_len(2);
let fsize: Vector2<f32> = size.cast(); let fsize: Vector2<f32> = size.cast();
let scale = if size.x < size.y { let stretch = if size.x < size.y {
Vector2::new(fsize.x / fsize.y, 1.0) Vector2::new(fsize.x / fsize.y, 1.0)
} else { } else {
Vector2::new(1.0, fsize.y / fsize.x) Vector2::new(1.0, fsize.y / fsize.x)
}; };
View { scale }
let mut scale = camera.zoom.mult().clone();
scale.set_precision(3);
Self::new(stretch, camera.zoom.level(), &scale, &x, &y)
} }
} }
unsafe impl bytemuck::Pod for View {}
unsafe impl bytemuck::Zeroable for View {}

View File

@@ -1,19 +1,23 @@
use super::{util::Uniform, View}; use super::{util::Storage, View};
pub struct TileLayout { pub struct TileLayout {
render_bind_layout: wgpu::BindGroupLayout, render_bind_layout: wgpu::BindGroupLayout,
render_pipeline_layout: wgpu::PipelineLayout, render_pipeline_layout: wgpu::PipelineLayout,
format: wgpu::TextureFormat, format: wgpu::TextureFormat,
pub view: Uniform<View>, pub view: Storage,
} }
impl TileLayout { impl TileLayout {
pub fn init(device: &wgpu::Device, config: &wgpu::SurfaceConfiguration) -> Self { pub fn init(device: &wgpu::Device, config: &wgpu::SurfaceConfiguration) -> Self {
let view = Uniform::init(device, "view"); let view = Storage::init_with(
device,
"view",
View::default().bytes(),
);
let render_bind_layout = let render_bind_layout =
device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
entries: &[view.bind_group_layout_entry(0)], entries: &[view.bind_group_layout_entry(0, true)],
label: Some("voxel render"), label: Some("voxel render"),
}); });

View File

@@ -36,7 +36,7 @@ impl TilePipeline {
camera: &Camera, camera: &Camera,
size: &Vector2<u32>, size: &Vector2<u32>,
) { ) {
self.view.update(device, encoder, belt, View::new(size)); self.view.update(device, encoder, belt, View::from_camera_size(camera, size).bytes());
} }
pub fn draw<'a>(&'a self, render_pass: &mut wgpu::RenderPass<'a>) { pub fn draw<'a>(&'a self, render_pass: &mut wgpu::RenderPass<'a>) {

View File

@@ -1,24 +1,29 @@
// Vertex shader const LEN: u32 = 3;
const ILEN: i32 = 3;
const LEN2: u32 = LEN * 2;
struct View {
stretch: vec2<f32>,
level: i32,
scale: FixedDec,
x: FixedDec,
y: FixedDec,
}
struct FixedDec {
sign: u32,
dec: i32,
parts: array<u32, LEN>,
}
@group(0) @binding(0)
var<storage> view: View;
struct VertexOutput { struct VertexOutput {
@builtin(position) vertex_pos: vec4<f32>, @builtin(position) vertex_pos: vec4<f32>,
@location(0) world_pos: vec2<f32>, @location(0) world_pos: vec2<f32>,
}; };
struct View {
scale: vec2<f32>,
// x_dec: i32,
// y_dec: i32,
// prec: u32,
}
@group(0) @binding(0)
var<uniform> view: View;
// @group(0) @binding(1)
// var<storage> vx: array<u32>;
// @group(0) @binding(2)
// var<storage> vy: array<u32>;
@vertex @vertex
fn vs_main( fn vs_main(
@builtin(vertex_index) vi: u32, @builtin(vertex_index) vi: u32,
@@ -33,34 +38,30 @@ fn vs_main(
out.vertex_pos = vec4<f32>(pos.x, -pos.y, 0.0, 1.0); out.vertex_pos = vec4<f32>(pos.x, -pos.y, 0.0, 1.0);
out.world_pos = pos; out.world_pos = pos;
out.world_pos.y *= -1.0; out.world_pos.y *= -1.0;
out.world_pos *= view.scale; out.world_pos *= view.stretch;
return out; return out;
} }
// const PREC = 2;
@fragment @fragment
fn fs_main( fn fs_main(
in: VertexOutput, in: VertexOutput,
) -> @location(0) vec4<f32> { ) -> @location(0) vec4<f32> {
let dec = i32(1) << 13; let cx = add(mul(from_f32(in.world_pos.x), view.scale), view.x);
let c = vec2<i32>(in.world_pos * f32(dec)); let cy = add(mul(from_f32(in.world_pos.y), view.scale), view.y);
let cx = c.x; var x = zero();
let cy = c.y; var y = zero();
var x = 0; let two = from_f32(2.0);
var y = 0; let thresh = from_f32(2.0 * 2.0);
var i = 0u; var i = 0u;
let thresh = 2 * dec; let max = 50u + (1u << u32(view.level));
let thresh2 = thresh * thresh;
let max = 50u;
loop { loop {
let x2 = x * x; let x2 = mul(x, x);
let y2 = y * y; let y2 = mul(y, y);
if x2 + y2 > thresh2 || i >= max { if gt(add(x2, y2), thresh) || i >= max {
break; break;
} }
y = (2 * x * y) / dec + c.y; y = add(mul(two, mul(x, y)), cy);
x = (x2 - y2) / dec + c.x; x = add(sub(x2, y2), cx);
i += 1u; i += 1u;
} }
var color = vec3<f32>(0.0, 0.0, 0.0); var color = vec3<f32>(0.0, 0.0, 0.0);
@@ -74,37 +75,247 @@ fn fs_main(
return vec4(color, 1.0); return vec4(color, 1.0);
} }
// @fragment fn add(lhs: FixedDec, rhs: FixedDec) -> FixedDec {
// fn fs_main( var dest = FixedDec();
// in: VertexOutput, dest.sign = lhs.sign;
// ) -> @location(0) vec4<f32> { dest.dec = max(lhs.dec, rhs.dec);
// let dec = i32(1) << 13; var carry = false;
// let c = vec2<i32>(in.world_pos * f32(dec)); let rhs_offset = rhs.dec - dest.dec;
// let cx = c.x; let lhs_offset = lhs.dec - dest.dec;
// let cy = c.y; var i = ILEN;
// var x = 0; if lhs.sign == rhs.sign {
// var y = 0; while i > 0 {
// var i = 0u; i -= 1;
// let thresh = 2 * dec; let a = at(lhs, i + lhs_offset);
// let thresh2 = thresh * thresh; let b = at(rhs, i + rhs_offset);
// let max = 50u + u32(in.zoom); let res = a + b + u32(carry);
// loop { dest.parts[i] = res;
// let x2 = x * x; carry = res < a;
// let y2 = y * y; }
// if x2 + y2 > thresh2 || i >= max { if carry {
// break; var i = ILEN - 1;
// } while i > 0 {
// y = (2 * x * y) / dec + c.y; i -= 1;
// x = (x2 - y2) / dec + c.x; dest.parts[i + 1] = dest.parts[i];
// i += 1u; }
// } dest.parts[0] = 1u;
// var color = vec3<f32>(0.0, 0.0, 0.0); dest.dec += 1;
// if i != max { }
// let pi = 3.1415; } else {
// let hue = f32(i) / 30.0; while i > 0 {
// color.r = cos(hue); i -= 1;
// color.g = cos(hue - 2.0 * pi / 3.0); let a = at(lhs, i + lhs_offset);
// color.b = cos(hue - 4.0 * pi / 3.0); let b = at(rhs, i + rhs_offset);
// } let res = a - b - u32(carry);
// return vec4(color, 1.0); dest.parts[i] = res;
// } carry = a < res;
}
if carry {
dest.sign = u32(dest.sign == 0);
var i = 0;
while i < ILEN {
dest.parts[i] = ~dest.parts[i];
i += 1;
}
}
}
return dest;
}
fn sub(lhs: FixedDec, rhs: FixedDec) -> FixedDec {
var r = rhs;
r.sign = u32(r.sign == 0);
return add(lhs, r);
}
fn at(dec: FixedDec, i: i32) -> u32 {
if i < 0 || i >= ILEN {
return 0u;
}
var parts = dec.parts;
return parts[i];
}
const POS: u32 = 0u;
const NEG: u32 = 1u;
fn mul(lhs: FixedDec, rhs: FixedDec) -> FixedDec {
let sign = u32(lhs.sign != rhs.sign);
var parts = array<u32, LEN2>();
var dec = lhs.dec + rhs.dec;
var lparts = lhs.parts;
var rparts = rhs.parts;
var i = LEN;
while i > 0 {
i -= 1u;
let x = lparts[i];
var carry: u32 = 0;
var j = LEN;
while j > 0 {
j -= 1u;
let y = rparts[j];
// widening mul
let lsb = x * y;
let a = x & 0xffff;
let b = x >> 16;
let c = y & 0xffff;
let d = y >> 16;
let ad = a * d + ((a * c) >> 16);
let bc = b * c;
let car = ad > (0xffffffff - bc);
let msb = ((ad + bc) >> 16) + (u32(car) << 16) + b * d;
let k = i + j + 1;
let res = parts[k] + lsb;
let carry1 = res < lsb;
let res2 = res + carry;
let carry2 = res2 < res;
parts[k] = res2;
carry = u32(carry1 || carry2) + msb;
}
parts[i] = carry;
}
var new_parts = array<u32, LEN>();
i = 0u;
while i < LEN2 && parts[i] == 0 {
dec -= 1;
i += 1u;
}
var j = 0u;
while j < LEN && (i + j) < LEN2 {
new_parts[j] = parts[i + j];
j += 1u;
}
return FixedDec(sign, dec, new_parts);
}
fn gt(x: FixedDec, y: FixedDec) -> bool {
if x.dec > y.dec {
return true;
}
if y.dec > x.dec {
return false;
}
return x.parts[0] > y.parts[0];
}
fn to_f32(value: FixedDec) -> f32 {
var parts = value.parts;
var sign = value.sign * (1u << 31);
var skip_count = 0;
while skip_count < ILEN && parts[skip_count] == 0 {
skip_count += 1;
}
if skip_count == ILEN {
if value.sign == POS {
return 0.0;
} else {
return -0.0;
}
}
let v = parts[skip_count];
var start = countLeadingZeros(v) + 1;
let exp_i = (value.dec - skip_count) * 32 - i32(start);
var frac_sh = 0u;
var exp = 0u;
if exp_i >= -127 {
if exp_i == -127 {
start -= 1u;
}
exp = u32(exp_i + 127);
} else {
frac_sh = u32(-(exp_i + 32 * 4 - 1));
if frac_sh < 23 {
start -= 1u;
} else {
return 0.0;
}
};
var frac: u32;
if start > 9 {
let sh = start - 9;
let next_i = skip_count + 1;
var v2 = 0u;
if next_i < ILEN {
v2 = parts[next_i] >> (32 - sh);
}
frac = (v << sh) + v2;
} else {
frac = v >> (9 - start);
};
frac &= ~(1u << 23);
let res = (frac >> frac_sh) + (exp << 23) + sign;
return bitcast<f32>(res);
}
const INV_SIGN_MASK: u32 = (1u << 31) - 1;
const FRAC_BIT: u32 = 1u << 23;
const FRAC_MASK: u32 = FRAC_BIT - 1;
fn from_f32(value: f32) -> FixedDec {
let raw = bitcast<u32>(value) & INV_SIGN_MASK;
var exp = i32(raw >> 23) - 127;
var frac = raw & FRAC_MASK;
var start = -exp;
if exp == -127 {
exp = -126;
start = -exp;
} else {
frac += FRAC_BIT;
start -= 1;
}
let end = -exp + 23;
let start_i = div_euclid(start, 32);
let end_i = div_euclid(end - 1, 32);
var parts = array<u32, LEN>();
var dec = -start_i;
if start_i == end_i {
let val = frac << u32(8 - rem_euclid(start, 32));
if val != 0 {
parts[0] = val;
}
} else {
let s = u32(rem_euclid(end, 32));
let val_high = frac >> s;
let val_low = frac << (32 - s);
var i = 0;
if val_high != 0 {
parts[0] = val_high;
i += 1;
} else {
dec -= 1;
}
if val_low != 0 {
parts[i] = val_low;
}
}
if parts[0] == 0 && parts[1] == 0 {
dec = 0;
}
return FixedDec(
u32(value < 0.0),
dec,
parts,
);
}
fn zero() -> FixedDec {
return FixedDec(0, 0, array<u32, LEN>());
}
fn div_euclid(x: i32, y: i32) -> i32 {
if x < 0 {
return -((-x - 1) / y) - 1;
}
return x / y;
}
fn rem_euclid(x: i32, y: i32) -> i32 {
return x - div_euclid(x, y) * y;
}

View File

@@ -5,8 +5,10 @@ mod texture;
mod timer; mod timer;
mod uniform; mod uniform;
mod array; mod array;
mod storage;
pub use texture::*; pub use texture::*;
pub use timer::*; pub use timer::*;
pub use uniform::*; pub use uniform::*;
pub use array::*; pub use array::*;
pub use storage::*;

View File

@@ -0,0 +1,71 @@
use std::marker::PhantomData;
use wgpu::util::DeviceExt;
pub struct Storage {
buffer: wgpu::Buffer,
}
impl Storage {
pub fn init<T: Default + bytemuck::Pod>(device: &wgpu::Device, name: &str) -> Self {
Self {
buffer: device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: Some(&(name.to_owned() + " Uniform Buf")),
contents: bytemuck::cast_slice(&[T::default()]),
usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST,
}),
}
}
pub fn init_with(device: &wgpu::Device, name: &str, data: &[u8]) -> Self {
Self {
buffer: device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: Some(&(name.to_owned() + " Uniform Buf")),
contents: data,
usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST,
}),
}
}
pub fn update(
&mut self,
device: &wgpu::Device,
encoder: &mut wgpu::CommandEncoder,
belt: &mut wgpu::util::StagingBelt,
data: &[u8],
) {
let mut view = belt.write_buffer(
encoder,
&self.buffer,
0,
unsafe {
std::num::NonZeroU64::new_unchecked(std::mem::size_of_val(data) as u64)
},
device,
);
view.copy_from_slice(data);
}
pub fn bind_group_layout_entry(
&self,
binding: u32,
read_only: bool,
) -> wgpu::BindGroupLayoutEntry {
wgpu::BindGroupLayoutEntry {
binding,
visibility: wgpu::ShaderStages::VERTEX
| wgpu::ShaderStages::FRAGMENT
| wgpu::ShaderStages::COMPUTE,
ty: wgpu::BindingType::Buffer {
ty: wgpu::BufferBindingType::Storage { read_only },
has_dynamic_offset: false,
min_binding_size: None,
},
count: None,
}
}
pub fn bind_group_entry(&self, binding: u32) -> wgpu::BindGroupEntry {
wgpu::BindGroupEntry {
binding,
resource: self.buffer.as_entire_binding(),
}
}
}

View File

@@ -1,4 +1,6 @@
#![feature(bigint_helper_methods)] #![feature(bigint_helper_methods)]
#![feature(int_roundings)]
#![feature(unbounded_shifts)]
use client::ClientApp; use client::ClientApp;

View File

@@ -45,15 +45,10 @@ impl From<f32> for FixedDec {
if parts.is_empty() { if parts.is_empty() {
dec = 0; dec = 0;
} }
let s = Self { Self {
sign: POS, sign: value.is_sign_negative(),
dec, dec,
parts, parts,
};
if value.is_sign_negative() {
-&s
} else {
s
} }
} }
} }
@@ -66,16 +61,8 @@ impl From<FixedDec> for f32 {
impl From<&FixedDec> for f32 { impl From<&FixedDec> for f32 {
fn from(value: &FixedDec) -> Self { fn from(value: &FixedDec) -> Self {
if value.is_zero() { let sign = if value.is_neg() { 1 << 31 } else { 0 };
return if value.sign == POS { 0.0 } else { -0.0 };
}
let mut sign = 0;
let value = if value.is_neg() {
sign = 1 << 31;
&-value
} else {
value
};
let mut skip_count = 0; let mut skip_count = 0;
let mut iter = value.parts.iter().peekable(); let mut iter = value.parts.iter().peekable();
@@ -85,7 +72,7 @@ impl From<&FixedDec> for f32 {
} }
let Some(v) = iter.next() else { let Some(v) = iter.next() else {
return 0.0; return if value.is_pos() { 0.0 } else { -0.0 };
}; };
let mut start = v.leading_zeros() + 1; let mut start = v.leading_zeros() + 1;
let exp_i = (value.dec - skip_count) * 32 - start as i32; let exp_i = (value.dec - skip_count) * 32 - start as i32;
@@ -115,3 +102,23 @@ impl From<&FixedDec> for f32 {
f32::from_bits(res) f32::from_bits(res)
} }
} }
impl From<u32> for FixedDec {
fn from(value: u32) -> Self {
Self {
dec: 1,
sign: POS,
parts: vec![value],
}
}
}
impl From<i32> for FixedDec {
fn from(value: i32) -> Self {
Self {
dec: 1,
sign: value.is_negative(),
parts: vec![value.try_into().unwrap_or((-value) as u32)],
}
}
}

View File

@@ -9,6 +9,14 @@ use std::fmt::{Binary, Display};
const POS: bool = false; const POS: bool = false;
const NEG: bool = true; const NEG: bool = true;
// dec is from the left instead of from the right
// because this is a fractal viewer, so it's expected
// that people zoom in instead of out; parts also has
// the most significant u32 at 0, so to zoom in you just
// have to add to the vec instead of insert at 0
// Might want to try to make it abstract over the direction
// of growth, or use a VecDeque, but doesn't seem worth for
// now
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub struct FixedDec { pub struct FixedDec {
sign: bool, sign: bool,
@@ -17,6 +25,10 @@ pub struct FixedDec {
} }
impl FixedDec { impl FixedDec {
pub fn from_parts(sign: bool, dec: i32, parts: Vec<u32>) -> Self {
Self { sign, dec, parts }
}
pub fn zeros() -> Self { pub fn zeros() -> Self {
Self::zero() Self::zero()
} }
@@ -41,11 +53,7 @@ impl FixedDec {
} }
pub fn trim(&mut self) { pub fn trim(&mut self) {
let rem_beg = self let rem_beg = self.parts.iter().take_while(|&&x| x == 0).count();
.parts
.iter()
.take_while(|&&x| x == 0)
.count();
self.parts.drain(0..rem_beg); self.parts.drain(0..rem_beg);
let rem_end = self.parts.iter().rev().take_while(|&&x| x == 0).count(); let rem_end = self.parts.iter().rev().take_while(|&&x| x == 0).count();
self.parts.truncate(self.parts.len() - rem_end); self.parts.truncate(self.parts.len() - rem_end);
@@ -55,6 +63,37 @@ impl FixedDec {
self.dec -= rem_beg as i32; self.dec -= rem_beg as i32;
} }
} }
pub fn set_whole_len(&mut self, len: i32) {
let diff = len - self.dec;
self.parts
.splice(0..usize::try_from(-diff).unwrap_or(0), (0..diff).map(|_| 0));
self.dec += diff;
if self.parts.is_empty() {
self.dec = 0;
}
}
pub fn set_dec_len(&mut self, len: i32) {
let len = usize::try_from(len + self.dec).unwrap_or(0);
self.parts.resize(len, 0);
if self.parts.is_empty() {
self.dec = 0;
}
}
pub fn set_precision(&mut self, prec: usize) {
self.parts.resize(prec, 0);
if self.parts.is_empty() {
self.dec = 0;
}
}
pub fn to_bytes(&self, bytes: &mut Vec<u8>) {
bytes.extend((self.sign as u32).to_le_bytes());
bytes.extend(self.dec.to_le_bytes());
bytes.extend(self.parts.iter().flat_map(|p| p.to_le_bytes()));
}
} }
impl Display for FixedDec { impl Display for FixedDec {
@@ -70,8 +109,9 @@ impl Binary for FixedDec {
} }
if self.dec < 0 { if self.dec < 0 {
write!(f, ".")?; write!(f, ".")?;
for _ in 0..(-self.dec) { write!(f, "00000000000000000000000000000000")?;
write!(f, "00000000000000000000000000000000")?; for _ in 0..(-self.dec - 1) {
write!(f, "_00000000000000000000000000000000")?;
} }
} }
for (i, part) in self.parts.iter().enumerate() { for (i, part) in self.parts.iter().enumerate() {
@@ -82,6 +122,12 @@ impl Binary for FixedDec {
} }
write!(f, "{:032b}", part)?; write!(f, "{:032b}", part)?;
} }
let diff = usize::try_from(self.dec)
.unwrap_or(0)
.saturating_sub(self.parts.len());
for _ in 0..diff {
write!(f, "_00000000000000000000000000000000")?;
}
Ok(()) Ok(())
} }
} }

View File

@@ -16,19 +16,19 @@ impl Zero for FixedDec {
} }
} }
impl Shr<u32> for FixedDec { impl Shr<i32> for FixedDec {
type Output = Self; type Output = Self;
fn shr(self, rhs: u32) -> Self::Output { fn shr(self, rhs: i32) -> Self::Output {
let mut parts = Vec::new(); let mut parts = Vec::with_capacity(self.parts.len());
let sr = rhs % 32; let sr = rhs.rem_euclid(32);
let sl = 32 - sr; let sl = 32 - sr as u32;
let mask = (1 << sr) - 1; let mask = (1 << sr) - 1;
let dec = self.dec - (rhs / 32) as i32; let dec = self.dec - rhs.div_floor(32);
let mut rem = 0; let mut rem = 0;
for part in self.parts { for part in self.parts {
parts.push((part >> sr) ^ rem); parts.push((part >> sr) ^ rem);
rem = (part & mask) << sl; rem = (part & mask).unbounded_shl(sl);
} }
if rem != 0 { if rem != 0 {
parts.push(rem); parts.push(rem);
@@ -98,22 +98,31 @@ impl Add for &FixedDec {
} }
} }
fn add(dest: &mut FixedDec, at: &impl Fn(usize) -> u32, rhs: &FixedDec) { fn add(dest: &mut FixedDec, src: &impl Fn(usize) -> u32, rhs: &FixedDec) {
let mut carry = false; let mut carry = false;
let same_sign = dest.sign == rhs.sign;
let rhs_offset = rhs.dec - dest.dec; let rhs_offset = rhs.dec - dest.dec;
for i in (0..dest.parts.len()).rev() { if dest.sign == rhs.sign {
let a = at(i); for i in (0..dest.parts.len()).rev() {
let b = rhs.part(i as i32 + rhs_offset); let a = src(i);
(dest.parts[i], carry) = carry_add(a, b, same_sign, carry); let b = rhs.part(i as i32 + rhs_offset);
} (dest.parts[i], carry) = a.carrying_add(b, carry);
if same_sign { }
if carry { if carry {
dest.parts.insert(0, 1); dest.parts.insert(0, 1);
dest.dec += 1; dest.dec += 1;
} }
} else if carry { } else {
dest.sign = !dest.sign for i in (0..dest.parts.len()).rev() {
let a = src(i);
let b = rhs.part(i as i32 + rhs_offset);
(dest.parts[i], carry) = a.borrowing_sub(b, carry);
}
if carry {
dest.sign = !dest.sign;
for part in &mut dest.parts {
*part = !*part;
}
}
} }
} }
@@ -125,16 +134,6 @@ fn new_dec(x: &FixedDec, y: &FixedDec) -> (i32, usize) {
(dec, len) (dec, len)
} }
fn carry_add(a: u32, b: u32, same_sign: bool, carry: bool) -> (u32, bool) {
if same_sign {
a.carrying_add(b, carry)
} else {
let (res, c) = a.overflowing_sub(b);
let (res, c2) = res.overflowing_sub(carry as u32);
(res, c || c2)
}
}
impl Sub for &FixedDec { impl Sub for &FixedDec {
type Output = FixedDec; type Output = FixedDec;
@@ -147,7 +146,7 @@ impl Sub for FixedDec {
type Output = Self; type Output = Self;
fn sub(mut self, rhs: Self) -> Self::Output { fn sub(mut self, rhs: Self) -> Self::Output {
self += &(-rhs); self -= &rhs;
self self
} }
} }
@@ -192,8 +191,7 @@ impl Mul for &FixedDec {
// let (lsb, msb) = mul_lmsb(x, y); // let (lsb, msb) = mul_lmsb(x, y);
let k = i + j + 1; let k = i + j + 1;
let (res, carry1) = parts[k].overflowing_add(lsb); let (res, carry1) = parts[k].overflowing_add(lsb);
parts[k] = res; let (res, carry2) = res.overflowing_add(carry);
let (res, carry2) = parts[k].overflowing_add(carry);
parts[k] = res; parts[k] = res;
// dude I have no clue if this can overflow; I know msb can take 1 without // dude I have no clue if this can overflow; I know msb can take 1 without
// overflowing, but I'm not sure if 2 can get here when it's max // overflowing, but I'm not sure if 2 can get here when it's max
@@ -216,6 +214,22 @@ impl Mul for FixedDec {
} }
} }
impl Mul<&FixedDec> for FixedDec {
type Output = FixedDec;
fn mul(self, rhs: &FixedDec) -> Self::Output {
&self * rhs
}
}
impl Mul<FixedDec> for &FixedDec {
type Output = FixedDec;
fn mul(self, rhs: FixedDec) -> Self::Output {
self * &rhs
}
}
fn mul_lmsb(x: u32, y: u32) -> (u32, u32) { fn mul_lmsb(x: u32, y: u32) -> (u32, u32) {
let lsb = x.wrapping_mul(y); let lsb = x.wrapping_mul(y);
let a = x & 0xffff; let a = x & 0xffff;

View File

@@ -1,37 +1,50 @@
use super::*; use super::*;
macro_rules! assert_bits_eq { const EPSILON: f32 = 0.000001;
macro_rules! assert_eq_f32 {
($left:expr, $right:expr, $dec:expr $(,)?) => { ($left:expr, $right:expr, $dec:expr $(,)?) => {
assert_eq_bits!($left, $left.to_bits(), $right, $right.to_bits(), $dec)
};
($left:expr, $right:expr, $dec:expr, $arg:tt, $($args:tt)+) => {
assert_eq_bits!($left, $left.to_bits(), $right, $right.to_bits(), $dec, $arg, $($args)+)
};
}
macro_rules! assert_eq_bits {
($left:expr, $left_bits:expr, $right:expr, $right_bits:expr, $dec:expr $(,)?) => {
assert!( assert!(
$left == $right, ($left - $right).abs() < EPSILON,
"\n left: {:032b} = {:?}\n right: {:032b} = {:?}\n from: {:?}", "\n left: {:032b} = {:?}\n right: {:032b} = {:?}\n from: {:?}",
$left.to_bits(), $left_bits,
$left, $left,
$right.to_bits(), $right_bits,
$right, $right,
$dec, $dec,
) )
}; };
($left:expr, $right:expr, $dec:expr, $arg:tt, $($args:tt)+) => { ($left:expr, $left_bits:expr, $right:expr, $right_bits:expr, $dec:expr, $arg:tt, $($args:tt)+) => {
assert!( assert!(
$left == $right, ($left - $right).abs() < EPSILON,
concat!("\n expr: ", $arg, "\n left: {:032b} = {:?}\n right: {:032b} = {:?}\n from: {:?}"), concat!("\n expr: ", $arg, "\n left: {:032b} = {:?}\n right: {:032b} = {:?}\n from: {:?}"),
$($args)+, $($args)+,
$left.to_bits(), $left_bits,
$left, $left,
$right.to_bits(), $right_bits,
$right, $right,
$dec, $dec,
) )
} };
} }
#[test] #[test]
fn conversion() { fn conversion() {
fn test(x: f32) { fn test(x: f32) {
let dec = FixedDec::from(x); let dec = FixedDec::from(x);
assert_bits_eq!(x, f32::from(&dec), dec) assert_eq_f32!(x, f32::from(&dec), dec)
} }
test(0.0);
test(-0.0);
test(f32::from_bits(0b00000000_00000000_00000000_00000001)); test(f32::from_bits(0b00000000_00000000_00000000_00000001));
test(f32::from_bits(0b00000000_01000000_00000000_00000001)); test(f32::from_bits(0b00000000_01000000_00000000_00000001));
test(f32::from_bits(0b00010000_01000000_00000000_00000001)); test(f32::from_bits(0b00010000_01000000_00000000_00000001));
@@ -53,44 +66,67 @@ fn conversion() {
#[test] #[test]
fn add_sub() { fn add_sub() {
fn test_add(x: f32, y: f32) { fn test(x: f32, y: f32) {
let a = x + y; let a = x + y;
let dec = FixedDec::from(x) + FixedDec::from(y); let dec = FixedDec::from(x) + FixedDec::from(y);
assert_bits_eq!(a, f32::from(&dec), dec, "{} + {}", x, y); assert_eq_f32!(a, f32::from(&dec), dec, "{} + {}", x, y);
} }
test_add(0.25, 0.75); test(0.25, 0.75);
test_add(1.25, 0.125); test(1.25, 0.125);
test_add(1.25, -0.125); test(1.25, -0.125);
test_add(100.25, -0.125); test(100.25, -0.125);
test_add(-1.25, 0.125); test(-1.25, 0.125);
test_add(-100.25, -0.125); test(-100.25, -0.125);
test_add(100.25, -0.125); test(100.25, -0.125);
// test_add(0.25, -0.00000000125); test(0.25, -0.00000000125);
test_add(0.25, -0.0000125); test(0.25, -0.0000125);
test_add(100000000000000.0, -100000000000.0); test(100000000000000.0, -100000000000.0);
test(0.0000310, -0.0042042);
test(-0.0000310, 0.0002042);
let x = -0.0016598016;
let y = 0.0028538946;
let mut a = FixedDec::from(x);
a.set_whole_len(1);
let b = FixedDec::from(y);
let res = a + b;
assert_eq_f32!(x + y, f32::from(&res), res, "{} + {}", x, y);
} }
#[test] #[test]
fn mul() { fn mul() {
fn test_mul(x: f32, y: f32) { fn test(x: f32, y: f32) {
let a = x * y; let a = x * y;
let dec = FixedDec::from(x) * FixedDec::from(y); let dec = FixedDec::from(x) * FixedDec::from(y);
assert_bits_eq!(a, f32::from(&dec), dec, "{:?} * {:?}", x, y); assert_eq_f32!(a, f32::from(&dec), dec, "{:?} * {:?}", x, y);
} }
test_mul(0.0, 0.0); test(0.0, 0.0);
test_mul(1.0, 1.0); test(1.0, 1.0);
test_mul(1.0, 0.0); test(1.0, 0.0);
test_mul(2.0, 1.0); test(2.0, 1.0);
test_mul(2.0, 0.5); test(2.0, 0.5);
test_mul(20.0, 0.245); test(20.0, 0.245);
test_mul(0.03819, 0.0183488); test(0.03819, 0.0183488);
test_mul(30492.39, 9130.391); test(30492.39, 9130.391);
test_mul(2.0, -1.0); test(2.0, -1.0);
test_mul(0.0, -1.0); test(0.0, -1.0);
test_mul(-1.0, 0.0); test(-1.0, 0.0);
test_mul(1.0, -1.0); test(1.0, -1.0);
test_mul(2.0, -1.20904); test(2.0, -1.20904);
test_mul(-249.0, -1.20904); test(-249.0, -1.20904);
test_mul(-30492.39, 9130.391); test(-30492.39, 9130.391);
test(-249.0, 0.000031421);
}
#[test]
fn shr() {
fn test(x: i32, y: i32) {
let a = (x as f32) / 2f32.powi(y);
let dec = FixedDec::from(x) >> y;
assert_eq_f32!(a, f32::from(&dec), dec, "{:?} * {:?}", x, y);
}
test(1, 3);
test(1, -3);
test(1, 33);
test(1, -33);
} }