diff --git a/src/pugixpath.cpp b/src/pugixpath.cpp
index 1e20d8cf16ff116c95c64b0cb470bc84a9484a56..d67eac60560fd751b71f96f678e4390469458637 100644
--- a/src/pugixpath.cpp
+++ b/src/pugixpath.cpp
@@ -497,35 +497,30 @@ namespace
 	#endif
 	}
 
-	double convert_string_to_number(const char_t* begin, const char_t* end)
+	bool convert_string_to_number(const char_t* begin, const char_t* end, double* out_result)
 	{
 		char_t buffer[32];
 
 		size_t length = static_cast<size_t>(end - begin);
+		char_t* scratch = buffer;
 
-		if (length < sizeof(buffer) / sizeof(buffer[0]))
-		{
-			// optimized on-stack conversion
-			memcpy(buffer, begin, length * sizeof(char_t));
-			buffer[length] = 0;
-
-			return convert_string_to_number(buffer);
-		}
-		else
+		if (length >= sizeof(buffer) / sizeof(buffer[0]))
 		{
 			// need to make dummy on-heap copy
-			char_t* copy = static_cast<char_t*>(get_memory_allocation_function()((length + 1) * sizeof(char_t)));
-			if (!copy) return gen_nan(); // $$ out of memory
+			scratch = static_cast<char_t*>(get_memory_allocation_function()((length + 1) * sizeof(char_t)));
+			if (!scratch) return false;
+		}
 
-			memcpy(copy, begin, length * sizeof(char_t));
-			copy[length] = 0;
+		// copy string to zero-terminated buffer and perform conversion
+		memcpy(scratch, begin, length * sizeof(char_t));
+		scratch[length] = 0;
 
-			double result = convert_string_to_number(copy);
+		*out_result = convert_string_to_number(scratch);
 
-			get_memory_deallocation_function()(copy);
+		// free dummy buffer
+		if (scratch != buffer) get_memory_deallocation_function()(scratch);
 
-			return result;
-		}
+		return true;
 	}
 	
 	double round_nearest(double value)
@@ -2899,7 +2894,10 @@ namespace pugi
 
 			case lex_number:
 			{
-				double value = convert_string_to_number(_lexer.contents().begin, _lexer.contents().end);
+				double value = 0;
+
+				if (!convert_string_to_number(_lexer.contents().begin, _lexer.contents().end, &value))
+					throw_error("Out of memory");
 
 				xpath_ast_node* n = new (alloc_node()) xpath_ast_node(ast_number_constant, xpath_type_number, value);
 				_lexer.next();
diff --git a/tests/test_xpath_parse.cpp b/tests/test_xpath_parse.cpp
index b8e46d93a3c723647182fbcb71be0a74164bcce1..f17357b4ccbe08da7eb3dd060d920e7204485925 100644
--- a/tests/test_xpath_parse.cpp
+++ b/tests/test_xpath_parse.cpp
@@ -269,15 +269,25 @@ TEST_XML(xpath_parse_absolute, "<div><s/></div>")
 	CHECK_XPATH_NODESET(doc, STR("/*[/]")) % 2;
 }
 
-TEST(xpath_parse_out_of_memory)
+TEST(xpath_parse_out_of_memory_first_page)
 {
 	test_runner::_memory_fail_threshold = 1;
 
 	CHECK_XPATH_FAIL(STR("1"));
+}
 
+TEST(xpath_parse_out_of_memory_second_page_node)
+{
 	test_runner::_memory_fail_threshold = 8192;
 
 	CHECK_XPATH_FAIL(STR("1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1"));
 }
 
+TEST(xpath_parse_out_of_memory_string_to_number)
+{
+	test_runner::_memory_fail_threshold = 4096 + 128;
+
+	CHECK_XPATH_FAIL(STR("0.11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111"));
+}
+
 #endif