หน้าเว็บ

วันจันทร์ที่ 23 พฤษภาคม พ.ศ. 2554

Double.NaN == Double.Nan ได้ false !!!

วันนี้เพิ่งรู้ว่า Double.NaN == Double.Nan จะได้ค่าเป็น false เสมอ (ทดสอบในภาษา Java)

ขอสรุปวิธีแก้ปัญหาไว้ตรงนี้เผื่อคนขี้เกียจอ่านครับ
ถ่าจะทดสอบว่าค่าหนึ่งๆเป็น NaN รึเปล่าจะใช้ x == Double.NaN ไม่ได้ แต่สามารถใช้ Double.isNaN(x) ได้แทน (ภาษา Java)


ส่วนคำอธิบายขอก๊อปปี้จาก twitter ตัวเองมาแปะตามลำดับเวลาล่ะกัน (ขี้เกียจเรียบเรียงเช่นกัน :P)

- ที่ไม่เท่ากันเกิดจากคอมไพเลอร์ทำตามมาตรฐาน floating point IEEE 754 ที่บอกว่า NaN ไม่เท่ากับ NaN

- จริงๆแล้ว bit pattern ที่ represent ค่า NaN นั้นไม่ได้มีค่าเดียว แต่มีเป็น range เลย

- โดย Double.NaN จะไปเรียกเมธอด Double.longBitsToDouble(0x7ff8000000000000L) ซึ่ง 0x7ff8000000000000L เป็น bit pattern หนึ่งที่เป็นไปได้

- ซึ่งถ้าดูผิวเผินแล้วเหมือนว่า Double.NaN กับ Double.NaN ก็น่าจะได้ค่าเดียวกัน เพราะส่ง bit pattern เดียวกันเข้าไป

- (เสริม NaN มี 2 แบบถ้า bit ขึ้นต้นด้วย 0 คือ signaling NaN (sNaN)ถ้าขึ้นต้นด้วย 1 คือ quiet NaN (qNaN) ดังนั้น 0x7ff8000000000000L คือ sNaN)

- ซึ่งถ้า sNaN ถูกกระทำการทางคณิตศาสตร์แล้วค่าจะเปลี่ยนเป็น qNaN ซึ่งค่าไม่เท่าเดิม

- ในขณะที่บางโปรเซสเซอร์ เพียงแค่มีการ copy ค่า (ในระดับโปรเซสเซอร์) ก็จะทำการแปลงค่าทันที

- นอกจากแปลงประเภทแล้ว bit pattern ก็อาจเปลี่ยนไปใน range ของ NaN ที่มีอยู่

- ดังนั้นการเรียก Double.NaN หรือ Double.longBitsToDouble(0x7ff8000000000000L) ก็อาจจะได้ค่า NaN ในรูปแบบ double ที่มีbit patternไม่เหมือนเดิม

- ดังนั้นถ่าจะทดสอบว่าค่าหนึ่งๆเป็น NaN รึเปล่าจะใช้ x == Double.NaN ไม่ได้ แต่สามารถใช้ Double.isNaN(x) ได้แทน (ภาษา Java)

อ้างอิง
Javadoc
- http://download.oracle.com/javase/1.4.2/docs/api/java/lang/Double.html#NaN
- http://download.oracle.com/javase/6/docs/api/java/lang/Double.html#longBitsToDouble(long)

IEEE 754 Standard
- http://en.wikipedia.org/wiki/IEEE_floating-point_standard
- http://en.wikipedia.org/wiki/NaN
- http://ieeexplore.ieee.org/xpl/freeabs_all.jsp?arnumber=4610935&con=yes&userType=inst

วันอาทิตย์ที่ 9 มกราคม พ.ศ. 2554

วิธี switch version ของ java บน ubuntu

ในกรณีที่เราลง java ไว้หลาย version เช่น 1.5, 1.6 แล้วเราต้องการเปลี่ยนเวอร์ชันให้ใช้คำสั่ง

sudo update-alternatives --config java

จะมีรายการ java ทั้งหมดที่มีในเครื่องขึ้นมาให้เลือก ก็ให้เลือกเลขที่ต้องการ เป็นอันเสร็จ

สามารถตรวจสอบว่าตอนนี้ version เป็นอะไรได้ด้วยคำสั่ง

java -version


ระบบที่ทดสอบ
OS: Ubuntu 10.10

ที่มา
http://ubuntuforums.org/showthread.php?t=655101

วันอังคารที่ 29 กรกฎาคม พ.ศ. 2551

การสร้าง Java class เพื่อใช้กับประเภทข้อมูลของ TinyOS

ในการเขียนโปรแกรมภาษา nesC นั้นส่วนใหญ่จะมีการสร้างประเภทตัวแปรไว้สำหรับจัดการกับ message ที่ส่งในระบบ หรือมีการประกาศค่าคงที่ต่างๆที่จะนำไปใช้ได้ โดยมักจะประกาศในไฟล์ .h หรือ header ไฟล์ เช่นเดียวกับภาษา C
แต่ถ้าเราจะเขียนโปรแกรมภาษาจาวาเพื่อทำการติดต่อข้อมูลกับระบบ sensor network ที่เขียนด้วยภาษา nesC นั้น เราก็จะต้องมีการสร้าง class บนภาษา Java เพื่อมารองรับ message นั้นๆ
ซึ่งใน TinyOS นั้นจะมี tool ที่ช่วยให้เราทำงานนี้ได้สบายขึ้น คือ MIG และ NCG

MIG
- Message Interface Generator
ตัวนี้จะเป็นตัวที่ใช้สร้าง class Java เพื่อจะรองรับ message ที่เราสร้างขึ้น

NCG- NesC Constant Generator
ตัวนี้จะทำหน้าที่ดึงค่า constant ต่างๆที่เราประกาศไว้ออกมาใส่ใน class Java

ก่อนอื่นผมขอสมมติว่า ผมทำ nesC app ไว้ที่ /opt/tinyos-1.x/apps/MyApp
โดยใน MyApp มีโฟลเดอร์ชื่่อ types ที่ใช้เก็บ header file ที่ใช้ประกาศประเภทข้อมูล message เอาไว้
และใน types มีไฟล์ชื่อ MyMsg.h ซึ่งภายในไฟล์มีการประกาศค่าคงที่ AM_TYPE และ message ไว้ คือ
// ไฟล์ MyMsg.h
enum AM_TYPE
{
AM_PINGMSG = 20,
AM_PINGREPLYMSG = 21,
};

typedef struct PingMsg
{
uint16_t hostid;
uint16_t seqno;
} PingMsg;

typedef struct PingReplyMsg
{
uint16_t hostid;
uint16_t seqno;
uint16_t origin;
uint16_t parent;
uint16_t depth;
} PingReplyMsg;

คราวนี้เราจะมาสร้าง class java โดยใช้ structure จากค่าเหล่านี้กัน

สมมติว่าเราจะสร้าง class java เอาไว้ใน MyApp/java ให้ทำการสร้างไฟล์ชื่อ Makefile เอาไว้ใน MyApp/java โดยมีเนื้อหาดังนี้ (สำหรับใครที่อ่านแล้วงงๆ ลองศึกษาเรื่องมีวิธีการทำ Makefile http://frank.mtsu.edu/~csdept/FacilitiesAndResources/make.htm)
// ไฟล์ Makefile
TOS = $(shell ncc -print-tosdir)
APPDIR = $(TOS)/../apps/MyApp

MIG = mig java
NCG = ncg java

MSGS = PingMsg.java ReplyPingMsg.java
CONST = PingConst.java

INITIAL_TARGETS = $(MSGS) $(CONST)

OTHER_CLEAN = cleanmig

ROOT = ../../../tools/java
include $(ROOT)/Makefile.include

PingMsg.java:
$(MIG) -java-classname=PingMsg $(APPDIR)/types/MyMsg.h PingMsg -o $@
$(JAVAC) $@

PingReplyMsg.java:
$(MIG) -java-classname=PingReplyMsg $(APPDIR)/types/MyMsg.h PingReplyMsg -o $@
$(JAVAC) $@

PingConst.java:
$(NCG) -java-classname=PingConst $(APPDIR)/types/MyMsg.h AM_PINGMSG AM_PINGREPLYMSG -o $@
$(JAVAC) $@

cleanmig:
rm -f $(MSGS) $(CONST)

โดยมีส่วนที่สำคัญคือ
MIG = mig java
NCG = ncg java

จะเป็นการสร้างค่าเพื่อใช้เรียกคำสั่ง mig และ ncg โดยให้สร้างเป็น class ประเภท java
MSGS = PingMsg.java ReplyPingMsg.java
CONST = PingConst.java

เป็นการระบุว่าเราจะสร้าง class ชื่ออะไรออกมาบ้าง ในที่นี่คือ PingMsg.java, ReplyPingMsg.java และ PingConst.java เพื่อเป็นประโยชน์เวลาสั่ง clean จะได้ตามลบได้หมดทุกตัว นั่นคือมันจะทำการ clean ทุกไฟล์ที่มีการประกาศไว้ใน MSGS และ CONST (ดูในโค้ด makefile ส่วนที่เป็น cleanmig ด้านล่างสุด) เพราะเวลาเราสั่ง make clean มันจะดูที่ค่าคงที่ OTHER_CLEAN ที่ประกาศไว้ ซึ่งในที่นี้เราให้เป็น cleanmig มันก็จะไปทำ cleanmig ซึ่งลบทุกอย่างที่ประกาศไว้ใน MSGS และ CONST นั่นเอง

include $(ROOT)/Makefile.include
ใช้ในการระบุ makefile ที่จะใช้ในการ compile java โดยไฟล์นี้จะชี้ไปที่ /opt/tinyos-1.x/tools/java/Makefile.include

PingMsg.java:
$(MIG) -java-classname=PingMsg $(APPDIR)/types/MyMsg.h PingMsg -o $@
$(JAVAC) $@

ทำการสร้างไฟล์ชื่อ PingMsg.java โดย
-java-classname เป็นการระบุชื่อไฟล์ java
$(APPDIR)/types/MyMsg.h เป็นการระบุ nesC header ไฟล์ที่เราต้องการจะดึงค่าออกมา
PingMsg เป็นชื่อ struct ที่เราประกาศไว้ใน nesC ไฟล์และจะเอามาทำเป็น java class
-o $@ สร้าง output ชื่อว่า PingMsg.java (@ คือชื่อที่เขียนไว้ที่บรรทัด PingMsg.java:)
$(JAVAC) $@ ทำการคอมไพล์ (ข้อควรระวัง เว้นวรรคด้านหน้านั้น ให้ใช้ tab(\t) หนึ่งครั้ง ห้ามเป็นเครื่องหมาย space ต่อกันหลายครั้ง)

ส่วนกรณีของ NCG นั้นก็จะคล้ายกัน แต่เปลียนจาก MIG เป็น NCG และสามารถระบุค่าคงที่ได้หลายตัวว่าจะเอาค่าอะไรออกมาบ้าง

พอสุดท้ายเราก็จะได้ PingMsg.java PingMsg.class PingReplyMsg.java PingReplyMsg.class PingConst.java และ PingConst.class เพื่อเอาไปใช้ในโปรแกรม java ของเราได้

ระบบที่ใช้ทดสอบ
TinyOS: 1.1.15 on cygwin

วันอังคารที่ 8 กรกฎาคม พ.ศ. 2551

Random Number ในภาษา Java แบบหยาบๆ

บังเอิญว่าไปเห็นคนถามใน soccersuck เลยไปช่วยตอบ ก็เลยเอามาประเดิมเป็นอันแรกซะเลย

จะ random ใช้ java.util.Random (jdk 1.4 ก็มีแล้ว)

Random gen = new Random();
ถ้าต้องการให้ค่าอยู่ในช่วงมากกว่าเท่ากับ 0 แต่น้อยกว่า n
ใช้ int randint = gen.nextInt(n);
เช่น int randint = gen.nextInt(6) จะได้ค่าตั้งแต่ 0 - 5

ถ้าต้องการตั้งแต่ 2 ถึง 8 ก็บวก offset เข้าไป

int randint = gen.nextInt(7) + 2;

ถ้าต้องการเป็น double เป็น float ก็จะมีคำสั่ง nextDouble nextFloat อยู่

(ป.ล. ทุกค่าที่ได้เป็น pseudo random number)

อ้างอิง
http://java.sun.com/j2se/1.4.2/docs/api/java/util/Random.html