diff --git a/crates/raw/src/dngwrite.rs b/crates/raw/src/dngwrite.rs
--- a/crates/raw/src/dngwrite.rs
+++ b/crates/raw/src/dngwrite.rs
@@ -241,12 +241,11 @@ pub fn write_dng(raw: &RawImage, opts: &DngWriteOptions) -> Result<Vec<u8>> {
     if !b.delta_v.is_empty() {
         ifd.set(t::BLACK_LEVEL_DELTA_V, Value::SRational(b.delta_v.iter().map(|&v| srational(v as f64)).collect()));
     }
-    let white: Vec<u32> = (0..cpp).map(|s| raw.white_at(s).round().clamp(0.0, u32::MAX as f32) as u32).collect();
-    if matches!(raw.data, RawData::F32(_)) {
-        ifd.set(t::WHITE_LEVEL, rat_vec(&(0..cpp).map(|s| raw.white_at(s) as f64).collect::<Vec<_>>()));
-    } else {
-        ifd.set(t::WHITE_LEVEL, Value::Long(white));
+    if !(0..cpp).all(|s| (raw.white_at(s) - raw.white_at(s).round()).abs() < 1e-4) {
+        return Err(RawError::Unsupported("fractional white level".into()));
     }
+    let white: Vec<u32> = (0..cpp).map(|s| raw.white_at(s).round().clamp(1.0, u32::MAX as f32) as u32).collect();
+    ifd.set(t::WHITE_LEVEL, Value::Long(white));
     let a = raw.active_area;
     ifd.set(t::ACTIVE_AREA, Value::Long(vec![a.y as u32, a.x as u32, (a.y + a.height) as u32, (a.x + a.width) as u32]));
     ifd.set(t::DEFAULT_CROP_ORIGIN, Value::Long(vec![raw.crop.x as u32, raw.crop.y as u32]));
diff --git a/crates/raw/tests/it/dng.rs b/crates/raw/tests/it/dng.rs
--- a/crates/raw/tests/it/dng.rs
+++ b/crates/raw/tests/it/dng.rs
@@ -172,6 +172,8 @@ fn deflate_roundtrips_integer_and_float() {
     raw.white = vec![1.0];
     for half in [false, true] {
         let bytes = write_dng(&raw, &DngWriteOptions { compression: DngCompression::Deflate { tile: 32, half }, ..Default::default() }).unwrap();
+        let tiff = lightcraft_tiff::Tiff::parse(&bytes).unwrap();
+        assert_eq!(tiff.ifds[0].value(t::WHITE_LEVEL), Some(&Value::Long(vec![1, 1, 1])));
         let back = decode(&bytes).unwrap();
         let RawData::F32(b) = &back.data else { panic!("float data expected") };
         for (a, b) in vals.iter().zip(b) {
@@ -435,7 +437,7 @@ fn non_finite_float_samples_decode_and_develop() {
     }
     raw.data = RawData::F32(vals.clone());
     raw.black = BlackLevel::uniform(0.0);
-    raw.white = vec![0.75];
+    raw.white = vec![1.0];
     for compression in [DngCompression::Uncompressed, DngCompression::Deflate { tile: 16, half: false }] {
         let bytes = write_dng(&raw, &DngWriteOptions { compression, ..Default::default() }).unwrap();
         let img = decode(&bytes).unwrap();
@@ -443,7 +445,7 @@ fn non_finite_float_samples_decode_and_develop() {
         let RawData::F32(got) = &img.data else { panic!("float data expected") };
         for (a, b) in vals.iter().zip(got) {
             let want = match *a {
-                f32::INFINITY => 0.75,
+                f32::INFINITY => 1.0,
                 v if v.is_finite() => v,
                 _ => 0.0,
             };
@@ -456,6 +458,15 @@ fn non_finite_float_samples_decode_and_develop() {
     }
 }
 
+#[test]
+fn fractional_white_level_is_rejected() {
+    let mut raw = synthetic(16, 12, None, 1);
+    raw.data = RawData::F32(vec![0.5; 16 * 12]);
+    raw.white = vec![0.75];
+    let err = write_dng(&raw, &DngWriteOptions::default()).unwrap_err();
+    assert!(matches!(err, lightcraft_raw::RawError::Unsupported(ref msg) if msg.contains("fractional white level")));
+}
+
 #[test]
 fn lj92_strips_and_levels_and_linearization() {
     let (w, h) = (20, 12);
diff --git a/crates/raw/tests/it/rendered_dng.rs b/crates/raw/tests/it/rendered_dng.rs
--- a/crates/raw/tests/it/rendered_dng.rs
+++ b/crates/raw/tests/it/rendered_dng.rs
@@ -18,6 +18,8 @@ fn rendered_dng_round_trips_pixels_and_colour() {
     for space in [SRGB, PROPHOTO] {
         let meta = lightcraft_meta::Metadata { make: Some("Maker".into()), model: Some("Model".into()), ..Default::default() };
         let bytes = write_rendered_dng(w, h, &rgb, &space, &meta, None).unwrap();
+        let tiff = lightcraft_tiff::Tiff::parse(&bytes).unwrap();
+        assert_eq!(tiff.ifds[0].value(lightcraft_tiff::tags::WHITE_LEVEL), Some(&lightcraft_tiff::Value::Long(vec![1, 1, 1])));
         let raw = lightcraft_raw::decode(&bytes).unwrap();
         assert_eq!((raw.width, raw.height, raw.cpp, raw.cfa.is_none()), (w, h, 3, true));
         assert_eq!(raw.metadata.model.as_deref(), Some("Model"));
