diff --git a/indra/llcommon/CMakeLists.txt b/indra/llcommon/CMakeLists.txt
index e1f2eb44fd9ad4ce8a071e7d4fe713961b869bfe..5b76703af7d3a53fb01163873b50d0d1013f0fc0 100644
--- a/indra/llcommon/CMakeLists.txt
+++ b/indra/llcommon/CMakeLists.txt
@@ -342,7 +342,7 @@ if (LL_TESTS)
   LL_ADD_INTEGRATION_TEST(llstring "" "${test_libs}")
   LL_ADD_INTEGRATION_TEST(lltreeiterators "" "${test_libs}")
   LL_ADD_INTEGRATION_TEST(lluri "" "${test_libs}")
-  LL_ADD_INTEGRATION_TEST(llunit "" "${test_libs}")
+  LL_ADD_INTEGRATION_TEST(llunits "" "${test_libs}")
   LL_ADD_INTEGRATION_TEST(reflection "" "${test_libs}")
   LL_ADD_INTEGRATION_TEST(stringize "" "${test_libs}")
   LL_ADD_INTEGRATION_TEST(lleventdispatcher "" "${test_libs}")
diff --git a/indra/llcommon/llfasttimer.cpp b/indra/llcommon/llfasttimer.cpp
index cf7655acf77531a7ef1f1f6027cc5f6293819c52..37e0fbac0af6596096a09e56f41e030593c6ea1f 100644
--- a/indra/llcommon/llfasttimer.cpp
+++ b/indra/llcommon/llfasttimer.cpp
@@ -166,7 +166,7 @@ U64 TimeBlock::countsPerSecond() // counts per second for the *64-bit* timer
 		firstcall = false;
 	}
 #endif
-	return sCPUClockFrequency;
+	return sCPUClockFrequency.value();
 }
 #endif
 
@@ -408,7 +408,7 @@ void TimeBlock::nextFrame()
 			}
 			call_count++;
 
-			LLUnit<LLUnits::Seconds, F64> total_time = 0;
+			LLUnit<LLUnits::Seconds, F64> total_time(0);
 			LLSD sd;
 
 			{
@@ -479,7 +479,7 @@ void TimeBlock::dumpCurTimes()
 		}
 
 		out_str << timerp->getName() << " " 
-			<< std::setprecision(3) << total_time_ms.as<LLUnits::Milliseconds, F32>() << " ms, "
+			<< std::setprecision(3) << total_time_ms.as<LLUnits::Milliseconds, F32>().value() << " ms, "
 			<< num_calls << " calls";
 
 		llinfos << out_str.str() << llendl;
diff --git a/indra/llcommon/llleap.cpp b/indra/llcommon/llleap.cpp
index 0a57ef1c48c57d32674e0e7f8f4ec4c377847581..84d2a12f657364de4c170673bdeda1bb2b88a209 100644
--- a/indra/llcommon/llleap.cpp
+++ b/indra/llcommon/llleap.cpp
@@ -394,7 +394,7 @@ class LLLeapImpl: public LLLeap
         LLProcess::WritePipe& childin(mChild->getWritePipe(LLProcess::STDIN));
         LLEventPump& mainloop(LLEventPumps::instance().obtain("mainloop"));
         LLSD nop;
-        F64 until(LLTimer::getElapsedSeconds() + 2);
+        F64 until = (LLTimer::getElapsedSeconds() + 2).value();
         while (childin.size() && LLTimer::getElapsedSeconds() < until)
         {
             mainloop.post(nop);
diff --git a/indra/llcommon/llprocessor.cpp b/indra/llcommon/llprocessor.cpp
index 6fe53396ca1ff0d8ef40a3ad3c0eb1e977d3eff9..5ddfa6fcef4ba99c32e258d1d6bfb96a912ae437 100644
--- a/indra/llcommon/llprocessor.cpp
+++ b/indra/llcommon/llprocessor.cpp
@@ -875,7 +875,7 @@ LLProcessorInfo::LLProcessorInfo() : mImpl(NULL)
 
 
 LLProcessorInfo::~LLProcessorInfo() {}
-LLUnit<LLUnits::Megahertz, F64> LLProcessorInfo::getCPUFrequency() const { return mImpl->getCPUFrequency(); }
+LLUnitImplicit<LLUnits::Megahertz, F64> LLProcessorInfo::getCPUFrequency() const { return mImpl->getCPUFrequency(); }
 bool LLProcessorInfo::hasSSE() const { return mImpl->hasSSE(); }
 bool LLProcessorInfo::hasSSE2() const { return mImpl->hasSSE2(); }
 bool LLProcessorInfo::hasAltivec() const { return mImpl->hasAltivec(); }
diff --git a/indra/llcommon/llprocessor.h b/indra/llcommon/llprocessor.h
index 2a21a5c115757dcd0fe5a4b2608edf409e7b144d..fbd427f48441bfbc9dda99740b43f416ff8438ee 100644
--- a/indra/llcommon/llprocessor.h
+++ b/indra/llcommon/llprocessor.h
@@ -37,7 +37,7 @@ class LL_COMMON_API LLProcessorInfo
 	LLProcessorInfo(); 
  	~LLProcessorInfo();
 
-	LLUnit<LLUnits::Megahertz, F64> getCPUFrequency() const;
+	LLUnitImplicit<LLUnits::Megahertz, F64> getCPUFrequency() const;
 	bool hasSSE() const;
 	bool hasSSE2() const;
 	bool hasAltivec() const;
diff --git a/indra/llcommon/lltimer.cpp b/indra/llcommon/lltimer.cpp
index 26063beff08d36f7a0253731ee1117378d261e98..838155d54dc5e79e916ca670848f276fe395d0c3 100644
--- a/indra/llcommon/lltimer.cpp
+++ b/indra/llcommon/lltimer.cpp
@@ -285,14 +285,14 @@ LLTimer::~LLTimer()
 }
 
 // static
-LLUnit<LLUnits::Microseconds, U64> LLTimer::getTotalTime()
+LLUnitImplicit<LLUnits::Microseconds, U64> LLTimer::getTotalTime()
 {
 	// simply call into the implementation function.
 	return totalTime();
 }	
 
 // static
-LLUnit<LLUnits::Seconds, F64> LLTimer::getTotalSeconds()
+LLUnitImplicit<LLUnits::Seconds, F64> LLTimer::getTotalSeconds()
 {
 	return U64_to_F64(getTotalTime()) * USEC_TO_SEC_F64;
 }
@@ -341,23 +341,23 @@ U64 getElapsedTimeAndUpdate(U64& lastClockCount)
 }
 
 
-LLUnit<LLUnits::Seconds, F64> LLTimer::getElapsedTimeF64() const
+LLUnitImplicit<LLUnits::Seconds, F64> LLTimer::getElapsedTimeF64() const
 {
 	U64 last = mLastClockCount;
 	return (F64)getElapsedTimeAndUpdate(last) * gClockFrequencyInv;
 }
 
-LLUnit<LLUnits::Seconds, F32> LLTimer::getElapsedTimeF32() const
+LLUnitImplicit<LLUnits::Seconds, F32> LLTimer::getElapsedTimeF32() const
 {
 	return (F32)getElapsedTimeF64();
 }
 
-LLUnit<LLUnits::Seconds, F64> LLTimer::getElapsedTimeAndResetF64()
+LLUnitImplicit<LLUnits::Seconds, F64> LLTimer::getElapsedTimeAndResetF64()
 {
 	return (F64)getElapsedTimeAndUpdate(mLastClockCount) * gClockFrequencyInv;
 }
 
-LLUnit<LLUnits::Seconds, F32> LLTimer::getElapsedTimeAndResetF32()
+LLUnitImplicit<LLUnits::Seconds, F32> LLTimer::getElapsedTimeAndResetF32()
 {
 	return (F32)getElapsedTimeAndResetF64();
 }
@@ -370,7 +370,7 @@ void  LLTimer::setTimerExpirySec(F32 expiration)
 		+ (U64)((F32)(expiration * gClockFrequency));
 }
 
-LLUnit<LLUnits::Seconds, F32> LLTimer::getRemainingTimeF32() const
+LLUnitImplicit<LLUnits::Seconds, F32> LLTimer::getRemainingTimeF32() const
 {
 	U64 cur_ticks = get_clock_count();
 	if (cur_ticks > mExpirationTicks)
diff --git a/indra/llcommon/lltimer.h b/indra/llcommon/lltimer.h
index 5cb2b181110099a1dfd6d2dfb02b6901f7cc7baf..0ba87d1e152693a2ceecace5e7ce66257d89f140 100644
--- a/indra/llcommon/lltimer.h
+++ b/indra/llcommon/lltimer.h
@@ -67,16 +67,16 @@ class LL_COMMON_API LLTimer
 
 	// Return a high precision number of seconds since the start of
 	// this application instance.
-	static LLUnit<LLUnits::Seconds, F64> getElapsedSeconds()
+	static LLUnitImplicit<LLUnits::Seconds, F64> getElapsedSeconds()
 	{
 		return sTimer->getElapsedTimeF64();
 	}
 
 	// Return a high precision usec since epoch
-	static LLUnit<LLUnits::Microseconds, U64> getTotalTime();
+	static LLUnitImplicit<LLUnits::Microseconds, U64> getTotalTime();
 
 	// Return a high precision seconds since epoch
-	static LLUnit<LLUnits::Seconds, F64> getTotalSeconds();
+	static LLUnitImplicit<LLUnits::Seconds, F64> getTotalSeconds();
 
 
 	// MANIPULATORS
@@ -87,16 +87,16 @@ class LL_COMMON_API LLTimer
 	void setTimerExpirySec(F32 expiration);
 	BOOL checkExpirationAndReset(F32 expiration);
 	BOOL hasExpired() const;
-	LLUnit<LLUnits::Seconds, F32> getElapsedTimeAndResetF32();	// Returns elapsed time in seconds with reset
-	LLUnit<LLUnits::Seconds, F64> getElapsedTimeAndResetF64();
+	LLUnitImplicit<LLUnits::Seconds, F32> getElapsedTimeAndResetF32();	// Returns elapsed time in seconds with reset
+	LLUnitImplicit<LLUnits::Seconds, F64> getElapsedTimeAndResetF64();
 
-	LLUnit<LLUnits::Seconds, F32> getRemainingTimeF32() const;
+	LLUnitImplicit<LLUnits::Seconds, F32> getRemainingTimeF32() const;
 
 	static BOOL knownBadTimer();
 
 	// ACCESSORS
-	LLUnit<LLUnits::Seconds, F32> getElapsedTimeF32() const;			// Returns elapsed time in seconds
-	LLUnit<LLUnits::Seconds, F64> getElapsedTimeF64() const;			// Returns elapsed time in seconds
+	LLUnitImplicit<LLUnits::Seconds, F32> getElapsedTimeF32() const;			// Returns elapsed time in seconds
+	LLUnitImplicit<LLUnits::Seconds, F64> getElapsedTimeF64() const;			// Returns elapsed time in seconds
 
 	bool getStarted() const { return mStarted; }
 
diff --git a/indra/llcommon/lltrace.h b/indra/llcommon/lltrace.h
index 6e6bb51e4704f85bbc294022e7c89b1ab5431531..25d95d9670c18819c0b50d5977b494768a170d0e 100644
--- a/indra/llcommon/lltrace.h
+++ b/indra/llcommon/lltrace.h
@@ -435,6 +435,9 @@ namespace LLTrace
 	class TraceType<TimeBlockAccumulator::CallCountAspect>
 	:	public TraceType<TimeBlockAccumulator>
 	{
+	public:
+		typedef F32 mean_t;
+
 		TraceType(const char* name, const char* description = "")
 		:	TraceType<TimeBlockAccumulator>(name, description)
 		{}
@@ -465,7 +468,7 @@ namespace LLTrace
 		void sample(UNIT_T value)
 		{
 			T converted_value(value);
-			getPrimaryAccumulator().sample((storage_t)converted_value);
+			getPrimaryAccumulator().sample(LLUnits::rawValue(converted_value));
 		}
 	};
 
@@ -484,7 +487,7 @@ namespace LLTrace
 		void add(UNIT_T value)
 		{
 			T converted_value(value);
-			getPrimaryAccumulator().add((storage_t)converted_value);
+			getPrimaryAccumulator().add(LLUnits::rawValue(converted_value));
 		}
 	};
 }
diff --git a/indra/llcommon/lltracerecording.h b/indra/llcommon/lltracerecording.h
index 6fd1a105d349a156ce84c15debc5e0517b9bd2b1..f92281cea8d558de80b411d3cd6286686dd5f9e6 100644
--- a/indra/llcommon/lltracerecording.h
+++ b/indra/llcommon/lltracerecording.h
@@ -205,7 +205,7 @@ namespace LLTrace
 		U32 getSampleCount(const TraceType<MeasurementAccumulator<F64> >& stat) const;
 		U32 getSampleCount(const TraceType<MeasurementAccumulator<S64> >& stat) const;
 
-		LLUnit<LLUnits::Seconds, F64> getDuration() const { return mElapsedSeconds; }
+		LLUnit<LLUnits::Seconds, F64> getDuration() const { return LLUnit<LLUnits::Seconds, F64>(mElapsedSeconds); }
 
 	private:
 		friend class ThreadRecorder;
diff --git a/indra/llcommon/llunit.h b/indra/llcommon/llunit.h
index 1f3ed0237c1f00b15b58902fd1a3abf888933cd7..6b023f82873b7494809aca73de27e08b82082e33 100644
--- a/indra/llcommon/llunit.h
+++ b/indra/llcommon/llunit.h
@@ -65,6 +65,7 @@ struct ConversionFactor<BASE_UNITS_TAG, BASE_UNITS_TAG, VALUE_TYPE>
 		return 1; 
 	}
 };
+
 }
 
 template<typename UNIT_TYPE, typename STORAGE_TYPE>
@@ -73,25 +74,25 @@ struct LLUnit
 	typedef LLUnit<UNIT_TYPE, STORAGE_TYPE> self_t;
 	typedef STORAGE_TYPE storage_t;
 
+	// value initialization
 	LLUnit(storage_t value = storage_t())
 	:	mValue(value)
 	{}
 
+	// unit initialization and conversion
 	template<typename OTHER_UNIT, typename OTHER_STORAGE>
 	LLUnit(LLUnit<OTHER_UNIT, OTHER_STORAGE> other)
 	:	mValue(convert(other))
 	{}
-
-	LLUnit(self_t& other)
-	:	mValue(other.mValue)
-	{}
-
+	
+	// value assignment
 	self_t& operator = (storage_t value)
 	{
 		mValue = value;
 		return *this;
 	}
 
+	// unit assignment
 	template<typename OTHER_UNIT, typename OTHER_STORAGE>
 	self_t& operator = (LLUnit<OTHER_UNIT, OTHER_STORAGE> other)
 	{
@@ -99,11 +100,6 @@ struct LLUnit
 		return *this;
 	}
 
-	operator storage_t() const
-	{
-		return value();
-	}
-
 	storage_t value() const
 	{
 		return mValue;
@@ -157,7 +153,7 @@ struct LLUnit
 	void operator /= (LLUnit<OTHER_UNIT, OTHER_STORAGE> divisor)
 	{
 		// spurious use of dependent type to stop gcc from triggering the static assertion before instantiating the template
-		llstatic_assert(sizeof(OTHER_UNIT) == 0, "Division of unit types not supported.");
+		llstatic_assert(sizeof(OTHER_UNIT) == 0, "Illegal in-place division of unit types.");
 	}
 
 	template<typename SOURCE_UNITS, typename SOURCE_STORAGE>
@@ -169,34 +165,30 @@ struct LLUnit
 	}
 
 protected:
-
 	storage_t mValue;
 };
 
 template<typename UNIT_TYPE, typename STORAGE_TYPE>
-struct LLUnitStrict : public LLUnit<UNIT_TYPE, STORAGE_TYPE>
+struct LLUnitImplicit : public LLUnit<UNIT_TYPE, STORAGE_TYPE>
 {
-	typedef LLUnitStrict<UNIT_TYPE, STORAGE_TYPE> self_t;
+	typedef LLUnitImplicit<UNIT_TYPE, STORAGE_TYPE> self_t;
 	typedef typename LLUnit<UNIT_TYPE, STORAGE_TYPE>::storage_t storage_t;
+	typedef LLUnit<UNIT_TYPE, STORAGE_TYPE> base_t;
 
-	explicit LLUnitStrict(storage_t value = storage_t())
-	:	LLUnit<UNIT_TYPE, STORAGE_TYPE>(value)
+	LLUnitImplicit(storage_t value = storage_t())
+	:	base_t(value)
 	{}
 
 	template<typename OTHER_UNIT, typename OTHER_STORAGE>
-	LLUnitStrict(LLUnit<OTHER_UNIT, OTHER_STORAGE> other)
-	:	LLUnit<UNIT_TYPE, STORAGE_TYPE>(convert(other))
+	LLUnitImplicit(LLUnit<OTHER_UNIT, OTHER_STORAGE> other)
+	:	base_t(convert(other))
 	{}
 
-	LLUnitStrict(self_t& other)
-	:	LLUnit<UNIT_TYPE, STORAGE_TYPE>(other)
-	{}
-
-
-private:
+	// unlike LLUnit, LLUnitImplicit is *implicitly* convertable to a POD scalar (F32, S32, etc)
+	// this allows for interoperability with legacy code
 	operator storage_t() const
 	{
-		return LLUnit<UNIT_TYPE, STORAGE_TYPE>::value();
+		return value();
 	}
 };
 
@@ -204,7 +196,7 @@ struct LLUnitStrict : public LLUnit<UNIT_TYPE, STORAGE_TYPE>
 // operator +
 //
 template<typename UNIT_TYPE1, typename STORAGE_TYPE1, typename UNIT_TYPE2, typename STORAGE_TYPE2>
-LLUnit<STORAGE_TYPE1, UNIT_TYPE1> operator + (LLUnit<UNIT_TYPE1, STORAGE_TYPE1> first, LLUnit<UNIT_TYPE2, STORAGE_TYPE2> second)
+LLUnit<UNIT_TYPE1, STORAGE_TYPE1> operator + (LLUnit<UNIT_TYPE1, STORAGE_TYPE1> first, LLUnit<UNIT_TYPE2, STORAGE_TYPE2> second)
 {
 	LLUnit<UNIT_TYPE1, STORAGE_TYPE1> result(first);
 	result += second;
@@ -227,6 +219,30 @@ LLUnit<UNIT_TYPE, STORAGE_TYPE> operator + (SCALAR_TYPE first, LLUnit<UNIT_TYPE,
 	return result;
 }
 
+template<typename UNIT_TYPE1, typename STORAGE_TYPE1, typename UNIT_TYPE2, typename STORAGE_TYPE2>
+LLUnitImplicit<UNIT_TYPE1, STORAGE_TYPE1> operator + (LLUnitImplicit<UNIT_TYPE1, STORAGE_TYPE1> first, LLUnit<UNIT_TYPE2, STORAGE_TYPE2> second)
+{
+	LLUnitImplicit<UNIT_TYPE1, STORAGE_TYPE1> result(first);
+	result += second;
+	return result;
+}
+
+template<typename UNIT_TYPE, typename STORAGE_TYPE, typename SCALAR_TYPE>
+LLUnitImplicit<UNIT_TYPE, STORAGE_TYPE> operator + (LLUnitImplicit<UNIT_TYPE, STORAGE_TYPE> first, SCALAR_TYPE second)
+{
+	LLUnitImplicit<UNIT_TYPE, STORAGE_TYPE> result(first);
+	result += second;
+	return result;
+}
+
+template<typename UNIT_TYPE1, typename STORAGE_TYPE1, typename UNIT_TYPE2, typename STORAGE_TYPE2>
+LLUnitImplicit<UNIT_TYPE1, STORAGE_TYPE1> operator + (LLUnitImplicit<UNIT_TYPE1, STORAGE_TYPE1> first, LLUnitImplicit<UNIT_TYPE2, STORAGE_TYPE2> second)
+{
+	LLUnitImplicit<UNIT_TYPE1, STORAGE_TYPE1> result(first);
+	result += second;
+	return result;
+}
+
 //
 // operator -
 //
@@ -238,7 +254,6 @@ LLUnit<UNIT_TYPE1, STORAGE_TYPE1> operator - (LLUnit<UNIT_TYPE1, STORAGE_TYPE1>
 	return result;
 }
 
-
 template<typename UNIT_TYPE, typename STORAGE_TYPE, typename SCALAR_TYPE>
 LLUnit<UNIT_TYPE, STORAGE_TYPE> operator - (LLUnit<UNIT_TYPE, STORAGE_TYPE> first, SCALAR_TYPE second)
 {
@@ -255,6 +270,30 @@ LLUnit<UNIT_TYPE, STORAGE_TYPE> operator - (SCALAR_TYPE first, LLUnit<UNIT_TYPE,
 	return result;
 }
 
+template<typename UNIT_TYPE1, typename STORAGE_TYPE1, typename UNIT_TYPE2, typename STORAGE_TYPE2>
+LLUnitImplicit<UNIT_TYPE1, STORAGE_TYPE1> operator - (LLUnitImplicit<UNIT_TYPE1, STORAGE_TYPE1> first, LLUnitImplicit<UNIT_TYPE2, STORAGE_TYPE2> second)
+{
+	LLUnitImplicit<UNIT_TYPE1, STORAGE_TYPE1> result(first);
+	result -= second;
+	return result;
+}
+
+template<typename UNIT_TYPE, typename STORAGE_TYPE, typename SCALAR_TYPE>
+LLUnitImplicit<UNIT_TYPE, STORAGE_TYPE> operator - (LLUnitImplicit<UNIT_TYPE, STORAGE_TYPE> first, SCALAR_TYPE second)
+{
+	LLUnitImplicit<UNIT_TYPE, STORAGE_TYPE> result(first);
+	result -= second;
+	return result;
+}
+
+template<typename UNIT_TYPE, typename STORAGE_TYPE, typename SCALAR_TYPE>
+LLUnitImplicit<UNIT_TYPE, STORAGE_TYPE> operator - (SCALAR_TYPE first, LLUnitImplicit<UNIT_TYPE, STORAGE_TYPE> second)
+{
+	LLUnitImplicit<UNIT_TYPE, STORAGE_TYPE> result(first);
+	result -= second;
+	return result;
+}
+
 //
 // operator *
 //
@@ -278,6 +317,26 @@ LLUnit<UNIT_TYPE1, STORAGE_TYPE1> operator * (LLUnit<UNIT_TYPE1, STORAGE_TYPE1>,
 	return LLUnit<UNIT_TYPE1, STORAGE_TYPE1>();
 }
 
+template<typename UNIT_TYPE, typename STORAGE_TYPE, typename SCALAR_TYPE>
+LLUnitImplicit<UNIT_TYPE, STORAGE_TYPE> operator * (SCALAR_TYPE first, LLUnitImplicit<UNIT_TYPE, STORAGE_TYPE> second)
+{
+	return LLUnitImplicit<UNIT_TYPE, STORAGE_TYPE>(first * second.value());
+}
+
+template<typename UNIT_TYPE, typename STORAGE_TYPE, typename SCALAR_TYPE>
+LLUnitImplicit<UNIT_TYPE, STORAGE_TYPE> operator * (LLUnitImplicit<UNIT_TYPE, STORAGE_TYPE> first, SCALAR_TYPE second)
+{
+	return LLUnitImplicit<UNIT_TYPE, STORAGE_TYPE>(first.value() * second);
+}
+
+template<typename UNIT_TYPE1, typename STORAGE_TYPE1, typename UNIT_TYPE2, typename STORAGE_TYPE2>
+LLUnitImplicit<UNIT_TYPE1, STORAGE_TYPE1> operator * (LLUnitImplicit<UNIT_TYPE1, STORAGE_TYPE1>, LLUnitImplicit<UNIT_TYPE2, STORAGE_TYPE2>)
+{
+	// spurious use of dependent type to stop gcc from triggering the static assertion before instantiating the template
+	llstatic_assert(sizeof(STORAGE_TYPE1) == 0, "Multiplication of unit types results in new unit type - not supported.");
+	return LLUnitImplicit<UNIT_TYPE1, STORAGE_TYPE1>();
+}
+
 //
 // operator /
 //
@@ -300,23 +359,42 @@ STORAGE_TYPE1 operator / (LLUnit<UNIT_TYPE1, STORAGE_TYPE1> first, LLUnit<UNIT_T
 	return STORAGE_TYPE1(first.value() / second.value());
 }
 
-#define COMPARISON_OPERATORS(op)                                                                     \
-template<typename UNIT_TYPE, typename STORAGE_TYPE, typename SCALAR_TYPE>                            \
-bool operator op (SCALAR_TYPE first, LLUnit<UNIT_TYPE, STORAGE_TYPE> second)                         \
-{                                                                                                    \
-	return first op second.value();                                                                  \
-}                                                                                                    \
-	                                                                                                 \
-template<typename UNIT_TYPE, typename STORAGE_TYPE, typename SCALAR_TYPE>                            \
-bool operator op (LLUnit<UNIT_TYPE, STORAGE_TYPE> first, SCALAR_TYPE second)                         \
-{                                                                                                    \
-	return first.value() op second;                                                                  \
-}                                                                                                    \
-	                                                                                                 \
-template<typename UNIT_TYPE1, typename STORAGE_TYPE1, typename UNIT_TYPE2, typename STORAGE_TYPE2>   \
-bool operator op (LLUnit<UNIT_TYPE1, STORAGE_TYPE1> first, LLUnit<UNIT_TYPE2, STORAGE_TYPE2> second) \
-{                                                                                                    \
-	return first.value() op first.convert(second);                                                   \
+template<typename UNIT_TYPE, typename STORAGE_TYPE, typename SCALAR_TYPE>
+LLUnitImplicit<UNIT_TYPE, STORAGE_TYPE> operator / (LLUnitImplicit<UNIT_TYPE, STORAGE_TYPE> first, SCALAR_TYPE second)
+{
+	return LLUnitImplicit<UNIT_TYPE, STORAGE_TYPE>(first.value() / second);
+}
+
+template<typename UNIT_TYPE1, typename STORAGE_TYPE1, typename UNIT_TYPE2, typename STORAGE_TYPE2>
+STORAGE_TYPE1 operator / (LLUnitImplicit<UNIT_TYPE1, STORAGE_TYPE1> first, LLUnitImplicit<UNIT_TYPE2, STORAGE_TYPE2> second)
+{
+	// spurious use of dependent type to stop gcc from triggering the static assertion before instantiating the template
+	return STORAGE_TYPE1(first.value() / second.value());
+}
+
+#define COMPARISON_OPERATORS(op)                                                                                     \
+template<typename UNIT_TYPE, typename STORAGE_TYPE, typename SCALAR_TYPE>                                            \
+bool operator op (SCALAR_TYPE first, LLUnit<UNIT_TYPE, STORAGE_TYPE> second)                                         \
+{                                                                                                                    \
+	return first op second.value();                                                                                  \
+}                                                                                                                    \
+	                                                                                                                 \
+template<typename UNIT_TYPE, typename STORAGE_TYPE, typename SCALAR_TYPE>                                            \
+bool operator op (LLUnit<UNIT_TYPE, STORAGE_TYPE> first, SCALAR_TYPE second)                                         \
+{                                                                                                                    \
+	return first.value() op second;                                                                                  \
+}                                                                                                                    \
+	                                                                                                                 \
+template<typename UNIT_TYPE1, typename STORAGE_TYPE1, typename UNIT_TYPE2, typename STORAGE_TYPE2>                   \
+bool operator op (LLUnitImplicit<UNIT_TYPE1, STORAGE_TYPE1> first, LLUnitImplicit<UNIT_TYPE2, STORAGE_TYPE2> second) \
+{                                                                                                                    \
+	return first.value() op first.convert(second);                                                                   \
+}                                                                                                                    \
+	                                                                                                                 \
+template<typename UNIT_TYPE1, typename STORAGE_TYPE1, typename UNIT_TYPE2, typename STORAGE_TYPE2>                   \
+	bool operator op (LLUnit<UNIT_TYPE1, STORAGE_TYPE1> first, LLUnit<UNIT_TYPE2, STORAGE_TYPE2> second)             \
+{                                                                                                                    \
+	return first.value() op first.convert(second);                                                                   \
 }
 
 COMPARISON_OPERATORS(<)
@@ -328,6 +406,15 @@ COMPARISON_OPERATORS(!=)
 
 namespace LLUnits
 {
+template<typename T>
+T rawValue(T val) { return val; }
+
+template<typename UNIT_TYPE, typename STORAGE_TYPE> 
+STORAGE_TYPE rawValue(LLUnit<UNIT_TYPE, STORAGE_TYPE> val) { return val.value(); }
+
+template<typename UNIT_TYPE, typename STORAGE_TYPE> 
+STORAGE_TYPE rawValue(LLUnitImplicit<UNIT_TYPE, STORAGE_TYPE> val) { return val.value(); }
+
 template<typename UNIT_TYPE, typename STORAGE_TYPE> 
 struct HighestPrecisionType<LLUnit<UNIT_TYPE, STORAGE_TYPE> >
 {
@@ -361,22 +448,22 @@ struct Bytes { typedef Bytes base_unit_t; };
 LL_DECLARE_DERIVED_UNIT(1024, Bytes, Kilobytes);
 LL_DECLARE_DERIVED_UNIT(1024 * 1024, Bytes, Megabytes);
 LL_DECLARE_DERIVED_UNIT(1024 * 1024 * 1024, Bytes, Gigabytes);
-LL_DECLARE_DERIVED_UNIT((1.0 / 8.0), Bytes, Bits);
-LL_DECLARE_DERIVED_UNIT((1024 / 8), Bytes, Kilobits);
-LL_DECLARE_DERIVED_UNIT((1024 / 8), Bytes, Megabits);
-LL_DECLARE_DERIVED_UNIT((1024 * 1024 * 1024 / 8), Bytes, Gigabits);
+LL_DECLARE_DERIVED_UNIT(1.0 / 8.0, Bytes, Bits);
+LL_DECLARE_DERIVED_UNIT(1024 / 8, Bytes, Kilobits);
+LL_DECLARE_DERIVED_UNIT(1024 / 8, Bytes, Megabits);
+LL_DECLARE_DERIVED_UNIT(1024 * 1024 * 1024 / 8, Bytes, Gigabits);
 
 struct Seconds { typedef Seconds base_unit_t; };
 LL_DECLARE_DERIVED_UNIT(60, Seconds, Minutes);
 LL_DECLARE_DERIVED_UNIT(60 * 60, Seconds, Hours);
-LL_DECLARE_DERIVED_UNIT((1.0 / 1000.0), Seconds, Milliseconds);
-LL_DECLARE_DERIVED_UNIT((1.0 / (1000000.0)), Seconds, Microseconds);
-LL_DECLARE_DERIVED_UNIT((1.0 / (1000000000.0)), Seconds, Nanoseconds);
+LL_DECLARE_DERIVED_UNIT(1.0 / 1000.0, Seconds, Milliseconds);
+LL_DECLARE_DERIVED_UNIT(1.0 / 1000000.0, Seconds, Microseconds);
+LL_DECLARE_DERIVED_UNIT(1.0 / 1000000000.0, Seconds, Nanoseconds);
 
 struct Meters { typedef Meters base_unit_t; };
 LL_DECLARE_DERIVED_UNIT(1000, Meters, Kilometers);
-LL_DECLARE_DERIVED_UNIT((1.0 / 100.0), Meters, Centimeters);
-LL_DECLARE_DERIVED_UNIT((1.0 / 1000.0), Meters, Millimeters);
+LL_DECLARE_DERIVED_UNIT(1.0 / 100.0, Meters, Centimeters);
+LL_DECLARE_DERIVED_UNIT(1.0 / 1000.0, Meters, Millimeters);
 
 struct Hertz { typedef Hertz base_unit_t; };
 LL_DECLARE_DERIVED_UNIT(1000, Hertz, Kilohertz);
diff --git a/indra/llcommon/tests/llunit_test.cpp b/indra/llcommon/tests/llunits_test.cpp
similarity index 75%
rename from indra/llcommon/tests/llunit_test.cpp
rename to indra/llcommon/tests/llunits_test.cpp
index a7e9c007404e079067a7e1d454244042db740812..2a941e8229b8071b90300b8eb10796a27f6c41e7 100644
--- a/indra/llcommon/tests/llunit_test.cpp
+++ b/indra/llcommon/tests/llunits_test.cpp
@@ -68,7 +68,7 @@ namespace tut
 		ensure(int_quatloos.value() == 42);
 
 		float_quatloos = 42.1f;
-		ensure(float_quatloos == 42.1f);
+		ensure(float_quatloos.value() == 42.1f);
 		int_quatloos = float_quatloos;
 		ensure(int_quatloos.value() == 42);
 		LLUnit<Quatloos, U32> unsigned_int_quatloos(float_quatloos);
@@ -153,4 +153,56 @@ namespace tut
 		quatloos -= LLUnit<Latinum, F32>(1.f);
 		ensure(quatloos.value() == 1);
 	}
+
+	// implicit units
+	template<> template<>
+	void units_object_t::test<5>()
+	{
+		// 0-initialized
+		LLUnit<Quatloos, F32> quatloos(0);
+		// initialize implicit unit from explicit
+		LLUnitImplicit<Quatloos, F32> quatloos_implicit = quatloos + 1;
+		ensure(quatloos_implicit.value() == 1);
+
+		// assign implicit to explicit, or perform math operations
+		quatloos = quatloos_implicit;
+		ensure(quatloos.value() == 1);
+		quatloos += quatloos_implicit;
+		ensure(quatloos.value() == 2);
+
+		// math operations on implicits
+		quatloos_implicit = 1;
+		ensure(quatloos_implicit == 1);
+
+		quatloos_implicit += 2;
+		ensure(quatloos_implicit == 3);
+
+		quatloos_implicit *= 2;
+		ensure(quatloos_implicit == 6);
+
+		quatloos_implicit -= 1;
+		ensure(quatloos_implicit == 5);
+
+		quatloos_implicit /= 5;
+		ensure(quatloos_implicit == 1);
+
+		quatloos_implicit = quatloos_implicit + 3 + quatloos_implicit;
+		ensure(quatloos_implicit == 5);
+
+		quatloos_implicit = 10 - quatloos_implicit - 1;
+		ensure(quatloos_implicit == 4);
+
+		quatloos_implicit = 2 * quatloos_implicit * 2;
+		ensure(quatloos_implicit == 16);
+
+		F32 one_half = quatloos_implicit / (quatloos_implicit * 2);
+		ensure(one_half == 0.5f);
+
+		// implicit conversion to POD
+		F32 float_val = quatloos_implicit;
+		ensure(float_val == 16);
+
+		S32 int_val = quatloos_implicit;
+		ensure(int_val == 16);
+	}
 }
diff --git a/indra/newview/llfasttimerview.cpp b/indra/newview/llfasttimerview.cpp
index 704b914b781c788278d24e50c234b991ca4ab2a9..1c63022527f7cc58b00607db8329441d49e17798 100644
--- a/indra/newview/llfasttimerview.cpp
+++ b/indra/newview/llfasttimerview.cpp
@@ -311,11 +311,11 @@ static std::string get_tooltip(LLTrace::TimeBlock& timer, S32 history_index, LLT
 	if (history_index < 0)
 	{
 		// by default, show average number of call
-		tooltip = llformat("%s (%d ms, %d calls)", timer.getName().c_str(), (S32)(frame_recording.getPeriodMean(timer) * ms_multiplier), (S32)frame_recording.getPeriodMean(timer.callCount()));
+		tooltip = llformat("%s (%d ms, %d calls)", timer.getName().c_str(), (S32)(frame_recording.getPeriodMean(timer) * ms_multiplier).value(), (S32)frame_recording.getPeriodMean(timer.callCount()));
 	}
 	else
 	{
-		tooltip = llformat("%s (%d ms, %d calls)", timer.getName().c_str(), (S32)(frame_recording.getPrevRecordingPeriod(history_index).getSum(timer) * ms_multiplier), (S32)frame_recording.getPrevRecordingPeriod(history_index).getSum(timer.callCount()));
+		tooltip = llformat("%s (%d ms, %d calls)", timer.getName().c_str(), (S32)(frame_recording.getPrevRecordingPeriod(history_index).getSum(timer) * ms_multiplier).value(), (S32)frame_recording.getPrevRecordingPeriod(history_index).getSum(timer.callCount()));
 	}
 	return tooltip;
 }
@@ -601,22 +601,22 @@ void LLFastTimerView::draw()
 	{
 		LLUnit<LLUnits::Milliseconds, U32> ms = total_time;
 
-		tdesc = llformat("%.1f ms |", (F32)ms*.25f);
+		tdesc = llformat("%.1f ms |", (F32)ms.value()*.25f);
 		x = xleft + barw/4 - LLFontGL::getFontMonospace()->getWidth(tdesc);
 		LLFontGL::getFontMonospace()->renderUTF8(tdesc, 0, x, y, LLColor4::white,
 										LLFontGL::LEFT, LLFontGL::TOP);
 			
-		tdesc = llformat("%.1f ms |", (F32)ms*.50f);
+		tdesc = llformat("%.1f ms |", (F32)ms.value()*.50f);
 		x = xleft + barw/2 - LLFontGL::getFontMonospace()->getWidth(tdesc);
 		LLFontGL::getFontMonospace()->renderUTF8(tdesc, 0, x, y, LLColor4::white,
 										LLFontGL::LEFT, LLFontGL::TOP);
 			
-		tdesc = llformat("%.1f ms |", (F32)ms*.75f);
+		tdesc = llformat("%.1f ms |", (F32)ms.value()*.75f);
 		x = xleft + (barw*3)/4 - LLFontGL::getFontMonospace()->getWidth(tdesc);
 		LLFontGL::getFontMonospace()->renderUTF8(tdesc, 0, x, y, LLColor4::white,
 										LLFontGL::LEFT, LLFontGL::TOP);
 			
-		tdesc = llformat( "%d ms |", (U32)ms);
+		tdesc = llformat( "%d ms |", (U32)ms.value());
 		x = xleft + barw - LLFontGL::getFontMonospace()->getWidth(tdesc);
 		LLFontGL::getFontMonospace()->renderUTF8(tdesc, 0, x, y, LLColor4::white,
 										LLFontGL::LEFT, LLFontGL::TOP);
@@ -728,11 +728,11 @@ void LLFastTimerView::draw()
 					++it)
 				{
 					sublevelticks += (tidx == -1)
-						? frame_recording.getPeriodMean(**it)
-						: frame_recording.getPrevRecordingPeriod(tidx).getSum(**it);
+						? frame_recording.getPeriodMean(**it).value()
+						: frame_recording.getPrevRecordingPeriod(tidx).getSum(**it).value();
 				}
 
-				F32 subfrac = (F32)sublevelticks / (F32)total_time;
+				F32 subfrac = (F32)sublevelticks / (F32)total_time.value();
 				sublevel_dx[level] = (int)(subfrac * (F32)barw + .5f);
 
 				if (mDisplayCenter == ALIGN_CENTER)
@@ -819,7 +819,7 @@ void LLFastTimerView::draw()
 		else if (mDisplayHz)
 			tdesc = llformat("%d Hz", (int)(1.f / max_time.value()));
 		else
-			tdesc = llformat("%4.2f ms", LLUnit<LLUnits::Milliseconds, F32>(max_time).value());
+			tdesc = llformat("%4.2f ms", max_time.value());
 							
 		x = mGraphRect.mRight - LLFontGL::getFontMonospace()->getWidth(tdesc)-5;
 		y = mGraphRect.mTop - LLFontGL::getFontMonospace()->getLineHeight();
@@ -900,7 +900,7 @@ void LLFastTimerView::draw()
 				F32 x = mGraphRect.mRight - j * (F32)(mGraphRect.getWidth())/(LLTrace::TimeBlock::HISTORY_NUM-1);
 				F32 y = mDisplayHz 
 					? mGraphRect.mBottom + (1.f / time.value()) * ((F32) mGraphRect.getHeight() / (1.f / max_time.value()))
-					: mGraphRect.mBottom + time * ((F32)mGraphRect.getHeight() / max_time);
+					: mGraphRect.mBottom + time / max_time * (F32)mGraphRect.getHeight();
 				gGL.vertex2f(x,y);
 				gGL.vertex2f(x,mGraphRect.mBottom);
 			}
@@ -920,22 +920,22 @@ void LLFastTimerView::draw()
 		}
 			
 		//interpolate towards new maximum
-		max_time = lerp((F32)max_time, (F32) cur_max, LLCriticalDamp::getInterpolant(0.1f));
+		max_time = lerp(max_time.value(), cur_max.value(), LLCriticalDamp::getInterpolant(0.1f));
 		if (max_time - cur_max <= 1 ||  cur_max - max_time  <= 1)
 		{
 			max_time = llmax(LLUnit<LLUnits::Microseconds, F32>(1), LLUnit<LLUnits::Microseconds, F32>(cur_max));
 		}
 
 		max_calls = lerp((F32)max_calls, (F32) cur_max_calls, LLCriticalDamp::getInterpolant(0.1f));
-		if (llabs(max_calls - cur_max) <= 1)
+		if (llabs((S32)(max_calls - cur_max_calls)) <= 1)
 		{
 			max_calls = cur_max_calls;
 		}
 
 		// TODO: make sure alpha is correct in DisplayHz mode
 		F32 alpha_target = (max_time > cur_max)
-			? llmin((F32) max_time/ (F32) cur_max - 1.f,1.f) 
-			: llmin((F32) cur_max/ (F32) max_time - 1.f,1.f);
+			? llmin(max_time / cur_max - 1.f,1.f) 
+			: llmin(cur_max/ max_time - 1.f,1.f);
 		alpha_interp = lerp(alpha_interp, alpha_target, LLCriticalDamp::getInterpolant(0.1f));
 
 		if (mHoverID != NULL)
diff --git a/indra/newview/pipeline.cpp b/indra/newview/pipeline.cpp
index 813fc7db6ac6aa8ff8df9e66e766a569d06d57b3..3be19c39202c57f1b09df5dd95defdd432b58ccc 100644
--- a/indra/newview/pipeline.cpp
+++ b/indra/newview/pipeline.cpp
@@ -2747,7 +2747,7 @@ void LLPipeline::updateGeom(F32 max_dtime)
 		
 	S32 count = 0;
 	
-	max_dtime = llmax(update_timer.getElapsedTimeF32()+0.001f, LLUnit<LLUnits::Seconds, F32>(max_dtime));
+	max_dtime = llmax(update_timer.getElapsedTimeF32()+0.001f, LLUnitImplicit<LLUnits::Seconds, F32>(max_dtime));
 	LLSpatialGroup* last_group = NULL;
 	LLSpatialBridge* last_bridge = NULL;