Adobe Air & Adobe Flex & ActionScript & Mobile Dev & HTML5 & RIA & User Experience
不管是用哪一種語言,只要關於浮點數運算一定會出現偏差
譬如 AS3 or Javascript 執行 152.2938 * 100
會輸出 15229.380000000001
當然最精確的作法是改用大數 BigDecimal 類別
假如一般不需要用到那麼精確,又要避開浮點數運算偏差的問題
最常見的作法可能就是直接用浮點數乘 100,四捨五入之後再除以 100 了
網路上很多現成的 function 可以用
function roundFloat(value:Number, divider:Number):Number{ return Math.round(value * divider) / divider; }
問題來了,那個除數大小到底該怎樣決定呢?
你可能會覺得很簡單,需要精確到小數下兩位就用 100,精確到三位就用 1000
可是這樣的用法正確嗎?
雙精度浮點數有效位數是 15-16 位
整數位數多,小數有效位數就減少,反之亦然
倘若整數占了 13 位,算式精確到小數 3 位只是自己騙自己而已
倘若整數占了 3 位,算式精確到小數 3 位則是浪費浮點數的有效位數
很明顯固定抓小數 3 位的作法無法適用絕大部分情況
假如需要計算整數位數少,小數位數多情況
就增加 divider 參數,反之就要縮小 divider
無法用同一個算式計算大小不同的數值
這樣寫法實在有點笨
而且上面 function 內容真的很少,為了這問題
所有的地方都要 import 並呼叫這個 function
好像有點過頭了
倘若全部都改成 inline 的算式好像又顯得雜亂
畢竟每個地方用到的 divider 大小可能不一樣
以下分享一個更簡單的作法,且能更有效的利用浮點數所有的有效位數
利用 Number.toPrecision 指定轉成固定精確度 15 的字串
這樣一轉,有效位數外的浮點數計算偏差就被去除掉了
然後再用 parseFloat 轉回浮點數
實際測試範例:
// 整數位數五位的情況,能夠進行到小數下 8 位的運算不受偏差影響 var no:Number = (152.2938 * 100) + 0.00000001; trace(no); // 15229.380000010002 <- 浮點數計算偏差 trace(parseFloat(no.toPrecision(15))); // 15229.38000001 <- 修正浮點數計算偏差
// 整數位數 10 位的情況,能夠進行到小數下 3 位的運算不受偏差影響 var no:Number = 1234567890; for (var i:int = 0 ; i < 9 ; ++i) { trace(no += 0.011); trace(no = parseFloat(no.toPrecision(15))); } /*/ 1234567890.011 1234567890.011 1234567890.0219998 <- 浮點數計算偏差 1234567890.022 <- 修正浮點數計算偏差 1234567890.033 1234567890.033 1234567890.044 1234567890.044 1234567890.0549998 <- 浮點數計算偏差 1234567890.055 <- 修正浮點數計算偏差 1234567890.066 1234567890.066 1234567890.077 1234567890.077 1234567890.0879998 <- 浮點數計算偏差 1234567890.088 <- 修正浮點數計算偏差 1234567890.099 1234567890.099 //*/
除了 Number.toPrecision 之外也可以使用 Number.toExponential
trace(Number(no.toExponential(15)));
AS3 不能用 toExponential,它似乎不會四捨五入
trace((0.3213).toExponential(10)); // 3.2129999999e-1
以上的方式簡單到根本沒有必要獨立寫成一個 function,也不需要設置不同參數
一律 inline 使用就好了
需要注意的是 Precision 的取捨,看運算的數值多大,需要決定不同的 Precision
最近 jQuery 更新到 1.6 版,雖然我沒有在用
可是還是測試看看新功能
結果發現新的 data 功能有 bug
jQuery 1.6 data() bug demo code:
<!DOCTYPE HTML> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <title>jQuery 1.6 data() Bug</title> <script type="text/javascript" src="jquery-1.6.js"></script> <script type="text/javascript"> $(function () { $("span").each(function(){ console.log(JSON.stringify($(this).data())); }); }); </script> </head> <body> <span data-x="data" /> <span data-x-x="data" /> <span data-x-x-x="data" /> <span data-x-xx-x="data" /> </body> </html>
用 Chrome 瀏覽器執行輸出結果
{"x":"data"} {"xX":"data"} {} {"xXxX":"data"}
實在令人搞不懂,為什麼 data attribute 這樣寫 data-x-x-x 就不能 parse 了呢?
其它三個類似的寫法卻又可以正常 work
上次發表了 Flex 4 Style Module 之後
有朋友問 Flex 3 能不能用,答案當然是不行
所以我又花了一些時間作出 Flex 3 版的 Style Module
本來以為 Flex 3 CSS 不支援 class selector, descendant selector… 會比較好改
結果不是那麼一回事
Flex 3 產生的 Style Module 一初始化馬上就執行 Style 設定,沒有放在特定介面的 function 內
這樣就算我撈到所有的 Module Style 還要自己想辦法找時機執行
另一個大問題是 Module 內還用了一個靜態旗標,表示 Style 是否初始化完畢
結果造成重覆載入兩次 Module 後,旗標為 true 無法撈到 Style,卸載時也無法還原 Style
還得要自行想辦法把旗標重設為 false~
以下是 Flex 3 版的 StyleManagerProxy:
package com.ticore.style.module { import flash.events.TimerEvent; import flash.utils.Timer; import flash.utils.setTimeout; import mx.styles.CSSStyleDeclaration; import mx.styles.StyleManagerImpl; public class StyleManagerProxy extends StyleManagerImpl { protected var styleModule:Object; protected var timer:Timer = new Timer(0, 1); protected var quene:Array = []; override public function StyleManagerProxy(styleModule:Object) { this.styleModule = styleModule; timer.addEventListener(TimerEvent.TIMER_COMPLETE, onTimerCompleteHandler); } // always return null, force style module call StyleManager.setStyleDeclaration(); override public function getStyleDeclaration(selector:String):CSSStyleDeclaration { return null; } override public function setStyleDeclaration(selector:String, styleDeclaration:CSSStyleDeclaration, update:Boolean):void { // delay execute until style factory setup quene.push([selector, styleDeclaration, update]); if (!timer.running) timer.start(); } protected function onTimerCompleteHandler(e:TimerEvent):void{ var args:Array; while (args = quene.shift()) { styleModule.setStyleDeclaration.apply(null, args); } } } }
Flex 3 版的 StyleManagerProxy:
package com.ticore.style.module { import flash.system.Security; import flash.utils.getDefinitionByName; import flash.utils.getQualifiedClassName; import mx.core.mx_internal; import mx.modules.ModuleBase; import mx.styles.CSSStyleDeclaration; import mx.styles.IStyleManager2; import mx.styles.IStyleModule; public class StyleModule extends ModuleBase implements IStyleModule { private static var domainsAllowed:Boolean = allowDomains(); private static function allowDomains():Boolean { if (Security.sandboxType != "application") { Security.allowDomain("*"); } return true; } //============================================================ public function StyleModule() { // reset static flag _MyStyleMod_StylesInit_done var fullName:String = getQualifiedClassName(this); var className:String = fullName.split("::")[1]; var Cls:Class = getDefinitionByName(fullName) as Class; var varName:String = "_" + className + "_StylesInit_done"; var qName:QName = new QName(mx_internal, varName); Cls[qName] = false; } protected var selectors:Array = []; protected var overrideMap:Object = {}; protected var effectMap:Object = {}; protected var unloadGlobal:Boolean; protected var styleManager:IStyleManager2; //============================================================ public static var StyleManagerCls:Class = mx.styles.StyleManager; // StyleManager class reference hook protected var StyleManager:Object = new StyleManagerProxy(this); public function getStyleDeclaration(selector:String):CSSStyleDeclaration { return StyleManagerCls.getStyleDeclaration(selector); } public function setStyleDeclaration(selector:String, style:CSSStyleDeclaration, update:Boolean):void { var newStyle:CSSStyleDeclaration = StyleManagerCls.getStyleDeclaration(selector); if (!newStyle) { newStyle = new CSSStyleDeclaration(); } // register override map and keys var factory:Function = style.factory; if (!(factory)) return; var dumpObj:Object = {}; factory.apply(dumpObj); var keys:Array = overrideMap[selector]; keys ||= []; overrideMap[selector] ||= keys; for (var key:* in dumpObj) { newStyle.mx_internal::setStyle(key, dumpObj[key]); keys.push(key); } // register effects map var addedEffects:Array; style.mx_internal::effects = style.mx_internal::effects || []; newStyle.mx_internal::effects = newStyle.mx_internal::effects || style.mx_internal::effects; addedEffects = style.mx_internal::effects.concat(); effectMap[selector] = addedEffects; // update style StyleManagerCls.setStyleDeclaration(selector, newStyle, true); } //============================================================ // original external style module functions public function unload():void { // (trace)("StyleModule.unload();"); unloadOverrides(); unloadStyleDeclarations(); if (unloadGlobal) { styleManager.stylesRoot = null; styleManager.initProtoChainRoots(); } } private function unloadOverrides():void { // (trace)("StyleModule.unloadOverrides();"); for (var selector:String in overrideMap) { // (trace)("selector:", selector); var style:CSSStyleDeclaration = StyleManagerCls.getStyleDeclaration(selector); if (style != null) { var keys:Array = overrideMap[selector]; var numKeys:int; var i:uint; if (keys != null) { numKeys = keys.length; for (i = 0; i < numKeys; i++) { // (trace)("clearOverride:", keys[i], style.getStyle(keys[i])); style.mx_internal::clearOverride(keys[i]); } } keys = effectMap[selector]; if (keys != null) { numKeys = keys.length; var index:uint; var effects:Array = style.mx_internal::effects; for (i = 0; i < numKeys; i++) { // ReferenceError: Error #1069: Number 上找不到屬性 0,而且沒有預設值。 // index = effects.indexOf(numKeys[i]); index = effects.indexOf(keys[i]); if (index >= 0) { effects.splice(index, 1); } } } } } overrideMap = null; effectMap = null; } private function unloadStyleDeclarations():void { // (trace)("StyleModule.unloadStyleDeclarations();"); var numSelectors:int = selectors.length; for (var i:int = 0; i < numSelectors; i++) { var selector:String = selectors[i]; styleManager.clearStyleDeclaration(selector, false); } selectors = null; } //============================================================ } }
GitHub 上我也作了分支 Flex 3.x StyleModule
文章来自36氪,下面是原文和我自己的感悟。
编者按:本文来自Chance Jiang的投稿,翻译自onstartups.com问答社区的一个Joel Spolsky的回帖。Joel Spolsky是曾是MS Excel产品的核心开发,目前是纽约软件公司Fog Creek的老板。他的博客Joel On Software是在业界流行了10多年的老博客,记录了Joel十几年行业经验,覆盖软件企业运作的点点滴滴,视为行业知识经典。
我有一个新的社会网络应用的构想。我并不期望这个应用可以获得巨大成功,但我想还是有些潜力的。我找过一些好友和同事交流过这个想法,他们都十分喜欢这个构想。还有些朋友甚至提出想作为合作伙伴加入一起进行开发,把想法变为一个可用的软件。
我无法用自己的钱给他们支付工资(他们也不期望那样),而且大家都打算把这项工作作为我们平时晚上或周末的业余项目。因为我认为这个想法有潜力可以 变为成功的企业,我想从目前这个阶段开始,就解决企业所有权/薪酬的问题,免得将来因为没界定清楚导致真正的问题。我倾向于把公司所有权在我们三个创始人 当中平分,而且基于这个分配规则来确定将来盈利后如何分配。这个选择对吗?如果对,怎么才算公平的股权分配?这是我自己提出的想法而且已花了不少时间做规 划(而且我很确定我将自己负担所有开发过程中发生的费用),所以我感觉我应该理所当然持有更大的股权。这个要求是否合理?
我还同时企图想办法按付出的努力来激励我的合伙人。我不担心有人加入后什么都不干,但我的确认为我们当中有一个或多个人也许将十分努力付出,比其他人付出更多。如果情况是这样,我想这类合伙人应该获得更多的股份。在规划股权架构方面,您有何建议?
这个问题实在是太普遍了,我打算对这个为题给出这个世界上最详细的回答。我希望,将来如果这个坛子上有人问到类似的问题,大家只需要引用我的回答。
最重要的(股权分配)原则:公平,而且可感知到的公平,比真正拥有大的股份更有价值。在一个创业公司,几乎所有 可能会出错的地方都会出错,而且会出错的问题当中最大最大的问题是创始人之间巨大的、令人气愤的、吵到面红耳赤的关于“谁更努力工作”的争论,谁拥有更多 股份,谁提出的想法等等。这也是我总会与一个朋友50-50平分一个新公司的股权,而不是坚持自己拥有60%的股权,因为“这是我的想法”,或者因为“我 比你更有经验”,或者任何其它原因。为什么呢?因为如果我把股权拆分为60-40,公司将在我们(创始人)不断争吵当中走向失败!如果你只是说,“去他妈 的,我们永远也无法知道正确的股权分配比例,我们还是像哥们儿那样50-50平分”,你们将继续是朋友而且公司将生存下去。
Alvin/Aedis.Ju:最重要的原则是公平,公平做起来真得不容易,有很多公司出现裂痕,合伙人出走,导致公司走向失败,都是因为利益分配不均引起的,特别是关于 idea & Exp 的争执上我觉得不用理会得非常好!去他妈的!
所以,我郑重向大家推出:Joel的适用于任何创业公司创始人完全公平划分股权的秘笈!
为简单起见,我将假设你们不打算拿风险投资,而且你们将不会有外来的投资人。随后,我再解释如何处理风险投资,但目前我们暂时假设没有投资人。同样 为简单起见,我们临时假设所有创始人都辞掉了他们的全职工作,而且同时开始全职为新公司工作。随后,我再解释如何处理后来加入的创始人。
来啦,原则是这样的:随着你们公司的成长,你们将一层一层/一批一批地加入新员工。公司的首批员工就是第一个创始人(或者第一批创始人)。也许有1 个,2个,3个或者更多,但你们都同时开始在新公司工作,而且你们要冒一样的风险……例如辞掉你们的工作加入一个未被市场认可的新公司。
第二批进来的人就是首个(批)真正的员工。当你聘任这批人时,你已从某个来源获得现金(投资人或者客户,这个无所谓)。这些人不需要冒多大风险因为他们从工作的第一天开始就拿了工资,而且,老实说,他们不是公司的创始人,他们是加入公司打工的。
第三批的人是更后来加入到员工。他们加入公司时,公司已运作得不错。
对于很多公司而言,每隔大约1年将进来一“批”员工。当你的公司规模大到可以卖给谷歌或上市或是其它,你公司员工也许已经有了6批:创始人1批,员 工大约5批。每一批员工人数都比上一批更多。也许有2个创始人,第二批当中有5名最早的员工,第三批有25名员工,而第四批有200名员工。越迟加入公司的员工需要冒的风险越低。
好啦,你将这样利用上述信息:创始人应该最终拿整个公司大约50%的股份。首层下面的5层员工的每一层最终都分别分到大约10%的公司股份,每一层的员工都将平分这10%的股份。
例子:
靠谱吗?你不必严格按照这个公式来规划股份,但基本思路是:你设立不同的资历“层”,最高的层级中的员工承受最大的风险,最低层层级的员工承担最少的风险,而每个“层”的员工平分公司分配给这个层级的股份,这个规则神奇地让越早加入到员工获得越多的股份。
Alvin/Aedis.Ju:让大家在工作的同时并且有一份归属感,这也是一个创业公司成熟的标志,不然都是打工,我干嘛来这里没日没夜的拼了老命的给你打工?给一定的股份也是我赞同的想法,当然,它需要Joel下面提到的“股份绑定”(vesting)。Joel的例子更加明确了员工在拿股份上的层次关系(每一层最终都分别分到大约10%的公司股份),第一年的4人拿250份股份,和第二年的20人拿50份股份…
使用“层级”的一个稍微不同的方式是“资历”。你的顶部层级是公司创始人,再下一层,你需要预留一整层给将来招聘牛逼哄哄并坚持需要10%股份的 CEO;再下一层是给那些早期进来的员工以及顶级经理人的,等等。无论你如何组织你的层级,它们应该是设计清晰明了,容易理解,不容易产生纷争。现在,你 搞定了一个公平的份股系统,但还有一个重要的原则:你必须执行“股份绑定”(vesting)。股份绑定期最好是4到5年。任何人都必须在公司做够起码1年才可持有股份(包括创始人)。好的股份绑定计划一般是头一年给25%,然后接下来每个月落实2%。否则,你的合作创始人将加入公司3个星期后跑掉,然后7年后又出现,并声称他拥有公司的25%的股份。没有“股份绑定”条款,你派股份给任何人都是不靠谱的!没有执行“股份绑定”是极其普遍的现象,后果可以十分严重。你看到有些公司的3个创始人没日没夜地工作了5年,然后你发现有些混蛋加入后2个星期就离开,这混蛋还以为他仍然拥有公司25%的股份,就因为他工作过的那2个星期。
Alvin/Aedis.Ju:股份绑定(vesting)是公司利益的保障,不过我觉得,如果我来分,一年后才给其中的一部分。而Joel建议的4-5年的时间期限显然比我一开始想的3年更加靠谱。
好了,让我们清理一下整个设计蓝图中没搞定的小问题。如果你的公司融资了,股份如何分割?投资可以来自任何方向,一个天使投资人,一个风险投资公司,或者是某人的老爸。基本上,回答很简单:新的投资将“稀释”所有人的股份。
沿用上面的例子,我们有2个创始人,我们给了自己每人2500股股份,所以我们每人拥有公司的50%股份,然后我们找了个风投,风投提出给我们 100万换取1/3的公司股份。公司1/3的股份 = 2500股。所以,你发行2500股给了风投。风投持有1/3公司股份,而你和另外一个创始人各持1/3。就这么多。如果并不是所有早期员工都需要拿工 资,怎么办?很多时候,有些公司创始人有不少个人积蓄,她决定公司启动后的某个阶段可以不拿工资。而有些创始人则需要现金,所以拿了工资。很多人认为不拿 工资的创始人可以多拿一些股份,作为创业初期不拿工资的回报。问题是,你永远不可能计算出究竟应该给多多少股份(作为初期不拿工资的回报)。这样做将导致 未来的纷争。千万不要用分配股权来解决这些问题。其实,你只需要针对每位创始人拿的工资做好记帐:不拿工资创始人就给 她记着工资“欠条”。当公司有了足够现金,就根据这个工资欠条补发工资给她。接下来的几年中,当公司现金收入逐步增加,或者当完成第一轮风险投资后,你可 以给每一位创始人补发工资,以确保每一位创始人都可从公司得到完全一样的工资收入。
创业构想是我提出的,难道我不应该多拿股份吗?不拿。构想基本上是不值钱的。仅仅因为提出创业构想就获得更多股 权,因此导致纷争是不值得的。如果你们当中有人首先提出的创业构想,但你们都同时辞工并同时开始创业,你们应该拿同等的股份。为公司工作才是创造价值的原 因,而你洗澡的时候突发奇想的“创业点子”根本不值什么钱。
如果创始人之一不是全职投入创业公司工作,该怎么办?那么,他(们)就不能算是“创始人”。在我的概念中,如果 一个人不全职投入公司的工作就不能算是创始人。任何边干着他们其它的全职工作边帮公司干活的人只能拿工资或者工资“欠条”,但是不要给股份。如果这个“创 始人”一直干着某份全职工作直到公司拿到风投,然后辞工全职过来公司干活,他(们)和第一批员工相比好不了多少,毕竟他们并没有冒其他创始人一样的风险。
如果有人为公司提供设备或其它有价值的东西(专利、域名等),怎么处理?很好啊。按这些东西的价值支付现金或开个“欠条”咯,别给股份。你准确算一下他给公司带来的那台电脑的价值,或者他们自带的某个聪明的字处理专利的价格,给他们写下欠条,公司有钱后再偿还即可。在创业初期就用股权来购买某些公司需要的东西将导致不平等,纷争和不公平。
投资人、创始人和雇员分别应该拥有多少股份?这都要看市场情况来确定。现实地看,如果投资人最终获得超过50% 的公司股权,创始人将感觉自己不重要而且会丧失动力,所以好的投资人也不会这样干(拿超过50%的股权)。如果公司能依赖自我积累来发展而不依靠外来投 资,创始人和员工一起将拥有公司100%的股权。有趣的是,这样的安排将给未来投资人带来足够的压力,以平衡投资人与创始人/员工。一条老经验是:公司上 市时(当你雇佣了足够的员工而且筹集了足够的投资后),投资人将拥有50%股份,创始人+员工将拥有50%股份,但是就2011年热门的网络公司而言,他 们的投资人最终拥有的股份都比50%少得多。
Alvin/Aedis.Ju:用欠条的手法有效的解决了股份分配的问题,又能很好的解决矛盾,又能保护公司的利益。投资人不会拿超过50%的股权,这是聪明的投资人的手法。
虽然创业公司股权分配原则这个问题没有一刀切的解决方案,但是你得尽可能让它简单化,透明化,直接了当,而最重要的是:要公平。只有这样你的公司才更有可能成功。
Alvin/Aedis.Ju:这是我目前为止看到的最好的如何分配股份问题的处理办法,如果有人需要创业,极为推荐!
Flex Formatter以前通过下载zip,解压缩到Flash Builder/Eclipse相应的plugins目录下进行安装。
目前,可以通过在Flash Builder中安装新软件的方式进行安装。
安装地址为
http://flexformatter.googlecode.com/svn/trunk/FlexFormatter/FlexPrettyPrintCommandUpdateSite
虽然早已经转移到sourceforge上面,但安装地址还是指向googlecode的svn的,而且测试过,拿到的都是最新版本。