diff --git a/builtin/common/serialize.lua b/builtin/common/serialize.lua
index 24b2a12ee3be9e2219ecdfbcf2f9393cf7710254..90b8b2ad6a6e40ab4a695e36e97f484b4579fd15 100644
--- a/builtin/common/serialize.lua
+++ b/builtin/common/serialize.lua
@@ -115,11 +115,20 @@ function core.serialize(x)
 	function dump_val(x)
 		local  tp = type(x)
 		if     x  == nil        then return "nil"
-		elseif tp == "number"   then return string.format("%d", x)
 		elseif tp == "string"   then return string.format("%q", x)
 		elseif tp == "boolean"  then return x and "true" or "false"
 		elseif tp == "function" then
 			return string.format("loadstring(%q)", string.dump(x))
+		elseif tp == "number"   then
+			-- Serialize integers with string.format to prevent
+			-- scientific notation, which doesn't preserve
+			-- precision and breaks things like node position
+			-- hashes.  Serialize floats normally.
+			if math.floor(x) == x then
+				return string.format("%d", x)
+			else
+				return tostring(x)
+			end
 		elseif tp == "table" then
 			local vals = {}
 			local idx_dumped = {}